引言
最近给实验室做的个系统需要用docker打包到对方服务器上,并提供多个URL页面以及接口,需求是要将提供的URL页面通过Iframe方式集成到他们的系统去,他们向我们的后端接口发送数据请求,渲染结果到我们的URL页面上,不了解WebSocket前把我难住了,我想到了设置时间间隔 不断向后端请求 也可以解决问题,但是并不优雅,请求数据过大时,时间间隔也不好设定。直到实验室李师兄通过WebSocket技术给我做了示范,原来前后端Http通信除了Ajax之外 还有WebSocket这种客户端和服务端的双向长连接通信。
WebSocket与Ajax轮询

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| var websocket = null;
if('WebSocket' in window){ websocket = new WebSocket(finalurlip); } else{ alert("浏览器不支持websocket,请更换浏览器!"); }
websocket.onerror = function() { setMessageInnerHTML("建立websocket连接出错!"); };
websocket.onopen = function(event) { }
websocket.onmessage = function(msg) { console.log(msg); data = JSON.parse(msg.data); var nodes = JSON.parse(data.nodes); var links = JSON.parse(data.links); console.log(nodes); console.log(links); var sankeyData = {}; sankeyData["nodes"] = nodes; sankeyData["links"] = links; vm2.sankeyData = sankeyData; console.log(vm2.sankeyData); vm2.setTopicSankey_2(1,"acl","数据演化图"); }
websocket.onclose = function() { setMessageInnerHTML("websocket连接关闭!"); }
window.onbeforeunload = function() { websocket.close(); }
function setMessageInnerHTML(innerHTML) { }
function closeWebSocket() { websocket.close(); }
function send() { var message = document.getElementById('text').value; websocket.send(message); }
|
后端Java代码
WebSocketConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.irlab.agriculture.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration public class WebSocketConfig {
@Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
|
WebSocketServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| package com.irlab.agriculture.service.acl;
import com.irlab.agriculture.controller.ACLController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint("/acl/websocket") @Component public class WebSocketServer {
private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
private static AtomicInteger onlineCount = new AtomicInteger(0);
private static CopyOnWriteArraySet<WebSocketServer> webSocketServers = new CopyOnWriteArraySet<>();
private Session session;
private String sid = "";
@OnOpen public void onOpen(Session session){ onlineCount.incrementAndGet(); this.session = session; sid = session.getId(); webSocketServers.add(this); logger.info("有新窗口开始监听:{},当前在线人数为{}",session.getId(),webSocketServers.size()); }
@OnClose public void onClose(){ onlineCount.decrementAndGet(); webSocketServers.remove(this); this.session = session; logger.info("有窗口关闭,当前在线人数为{}",webSocketServers.size()); }
@OnMessage public void onMessage(String message,Session session){ this.session = session; logger.info("收到来自窗口:{}的信息:{}",sid,message);
}
@OnError public void onError(Session session,Throwable error){ logger.error("websocket发生错误:",error); }
public void sendMessage(String message) throws IOException{ this.session.getBasicRemote().sendText(message); }
public static void sendInfoToMany(String info) throws IOException{ logger.info("推送消息到多个窗口"); if(webSocketServers.size()==0){ logger.error("没有注册的窗口!"); return; } for(WebSocketServer item:webSocketServers){ try{ item.sendMessage(info); } catch (IOException e){ continue; }
} }
public static CopyOnWriteArraySet<WebSocketServer> getWebSocketServers(){ return webSocketServers; }
}
|