- SpringBoot整合WebSocket过程请查看 SpringBoot之WebSocket服务搭建;
- 以下WebSocket兼容IE8浏览器的方式就是在 SpringBoot之WebSocket服务搭建基础上完成的。
为了兼容IE8浏览器,WebSocket采用Adobe Flash来实现,而Flash由于安全策略问题,会向TCP:843端口发送请求文件,服务器应该返回安全策略文件给客户端;
这里是使用开源项目web-socket-js对IE8中使用webSocket进行兼容适配与实现;
web-socket-js官网地址:https://github.com/gimite/web-socket-js
从官网中下载并提取
swfobject.js
、web_socket.js
、WebSocketMain.swf
等三个文件
将上面三个文件放在SpringBoot静态资源目录下
- 在·src\main\resources\static
目录下新建
myjs文件夹,并将
swfobject.js与
web_socket.js`放在myjs中- 将
WebSocketMain.swf
放在跟目录src\main\resources\
下
此配置方式只需要两步:
- 编写一个flash安全服务策略文件监听843端口的监听器线程类;
- 在spring boot应用启动后,随即启动监听843端口的监听器线程;
- 配置flash通信协议的策略文件
兼容IE8方式1:将crossdomain.xml改名后放在跟目录下的方式
package org.yuan.mysoket;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 兼容IE8方式1:将crossdomain.xml改名后放在跟目录下的方式
* 为了兼容IE8浏览器,WebSocket采用Adobe Flash来实现,而Flash由于安全策略问题,会向TCP:843端口发送请求文件,服务器应该返回安全策略文件给客户端。
*/
public class WebSocketPolicyListener implements Runnable {
private ServerSocket server;
public WebSocketPolicyListener() {
try {
server = new ServerSocket(843);
System.out.println("Adobe Flash安全策略监听启动");
} catch (IOException e) {
System.err.println();
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
System.out.println("线程run");
try {
Socket s = server.accept();
//获取安全策略请求文件
BufferedInputStream bin = new BufferedInputStream(s.getInputStream());//打开文件流//
//网络操作调用avaiable()方法时,可能数据并没有到达,这时需要等待数据变为可用
int avaiable = 0;
//当有可用数据时,退出循环
while (avaiable == 0) {
avaiable = bin.available();
}
byte[] buf = new byte[avaiable];
int count = bin.read(buf);
String request = new String(buf, 0, count);
System.out.println("安全策略文件请求:\n" + request);
//如果是安全策略文件请求,将安全策略文件发给客户端
if (request.indexOf(" ") != -1) {
//获取文件路径,我放在src目录下
String path = WebSocketPolicyListener.class.getResource("/").getPath() + "websocket-policy.xml";
FileInputStream fin = new FileInputStream(path);
buf = new byte[1024];
StringBuilder sb = new StringBuilder();
while ((count = fin.read(buf)) > 0) {
s.getOutputStream().write(buf, 0, count);
sb.append(new String(buf, 0, count));
}
System.out.println("安全策略文件响应:\n" + sb.toString());
fin.close();//关闭文件流
}
s.close();//关闭Socket流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现CommandLineRunner接口,并在run方法中启动WebSocketPolicyListener
package org.yuan.config;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.yuan.mysoket.WebSocketPolicyListener;
@Component
public class InitConfig implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
WebSocketPolicyListener webSocketPolicyListener = new WebSocketPolicyListener();
new Thread(webSocketPolicyListener).start();
}
}
默认的策略文件名为
crossdomain.xml
;
这里我自己定义为
ebsocket-policy.xml
,并将此文件放在项目跟目录下;如
src\main\resources\websocket-policy.xml
websocket-policy.xml
内容如下
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-http-request-headers-from domain="*" headers="*"/>
<allow-access-from domain="*" to-ports="2001"/>
cross-domain-policy>
SocketService.java
类如下J
package org.yuan.config;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 兼容IE8方式2:将crossdomain.xml中的内容直接写入Java代码中
* 这个socket主要还是为了flash的socket
*/
public class SocketService {
private ServerSocket serverSocket = null;
private static Object locker = new Object();
// 线程池
private static ExecutorService executorService = null;
public synchronized void initSocketServer() {
try {
if (executorService == null) {
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 20);
} else {
return;
}
//启动843端口
serverSocket = new ServerSocket(843);
Runnable runnable = new Server();
Thread thread = new Thread(runnable);
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void closeServer() {
locker = null;
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Server implements Runnable {
@Override
public void run() {
try {
while (locker != null) {
if (serverSocket == null || serverSocket.isClosed()) {
continue;
}
//接收客户端的连接
Socket incoming = serverSocket.accept();
Runnable runnable = new ThreadClient(incoming);
executorService.execute(runnable);
}
} catch (Exception e) {
//此处有一种异常,当关掉Tomcat时,会去关闭ServerSocket对象,
//从而抛出异常java.net.SocketException: socket closed,
//原因是ServerSocket对象正在等待客户端连接或正在连接中
e.printStackTrace();
}
}
}
class ThreadClient implements Runnable {
private Socket incoming;
private ThreadClient(Socket socket) {
incoming = socket;
}
public void run() {
InputStreamReader isr = null;
BufferedReader br = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
isr = new InputStreamReader(incoming.getInputStream(), "UTF-8");
br = new BufferedReader(isr);
osw = new OutputStreamWriter(incoming.getOutputStream(), "UTF-8");
bw = new BufferedWriter(osw);
//读取客户端发送的数据
StringBuilder sb = new StringBuilder();
int c;
while ((c = br.read()) != -1) {
if (c != '\0') {
sb.append((char) c);
} else {
break;
}
}
String info = sb.toString();
System.out.println(String.format("客户端发送的数据:%s", info));
//接收到客户端请求之后,将策略文件发送出去
if (info.contains(" ")) {
bw.write(" \0");
bw.flush();
System.out.println(String.format("将安全策略文件发送至:%s", incoming.getInetAddress()));
} else {
bw.write("请求无法识别\0");
bw.flush();
System.out.println(String.format("请求无法识别:%s", incoming.getInetAddress()));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (isr != null) {
isr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (osw != null) {
osw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (incoming != null) {
incoming.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 实现
ServletContextListener
监听接口并在实现方法中启动843端口监听类;- 编写MyServletContextListener类让其实现ServletContextListener接口,并实现其中的两个方法
MyServletContextListener.java
具体内容如下
package org.yuan.config;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
*
* Description:
*
* Author:jinshengyuan
* Datetime: 2020-06-16 15:25
*
*
*/
@WebListener
public class MyServletContextListener implements ServletContextListener {
private SocketService service;
@Override
public void contextInitialized(ServletContextEvent sce) {
service = new SocketService();
service.initSocketServer();
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
service.closeServer();
}
}
index.html
内容如下
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Sample of web_socket.jstitle>
<script type="text/javascript" src="/myjs/swfobject.js">script>
<script type="text/javascript" src="/myjs/web_socket.js">script>
<script type="text/javascript">
// Set URL of your WebSocketMain.swf here:
WEB_SOCKET_SWF_LOCATION = "WebSocketMain.swf";
// Set this to dump debug message from Flash to console.log:
WEB_SOCKET_DEBUG = true;
// Everything below is the same as using standard WebSocket.
var ws;
function init() {
// Connect to Web Socket.
// Change host/port here to your own Web Socket server.
ws = new WebSocket("ws://localhost:2001/myChat");
// Set event handlers.
ws.onopen = function() {
output("onopen");
};
ws.onmessage = function(e) {
// e.data contains received string.
output("onmessage: " + e.data);
};
ws.onclose = function() {
output("onclose");
};
ws.onerror = function() {
output("onerror");
};
}
function onSubmit() {
var input = document.getElementById("input");
// You can send message to the Web Socket using ws.send.
ws.send(input.value);
output("send: " + input.value);
input.value = "";
input.focus();
}
function onCloseClick() {
ws.close();
}
function output(str) {
var log = document.getElementById("log");
var escaped = str.replace(/&/, "&").replace(/</, "<").
replace(/>/, ">").replace(/"/, """); // "
log.innerHTML = escaped + "
" + log.innerHTML;
}
script>
head><body onload="init();">
<form onsubmit="onSubmit(); return false;">
<input type="text" id="input">
<input type="submit" value="Send">
<button onclick="onCloseClick(); return false;">closebutton>
form>
<div id="log">div>
body>html>
启动SpringBoot应用后,浏览器中输入地址进行测试:http://localhost:2001