websocket详解


前言

    传统的web项目都是客户端发送请求,服务器响应这种单工模式,但是如果需要服务器端主动发送数据就做不到,于是就有了Websocket。

知识概念

一、什么是Websocket?

WebSocket是一种在单个TCP连接上进行全双工通信的协议

二、Websocket协议和Http协议


Http协议:Http协议是个无连接协议,Http1.0不支持持久连接即一次连接只能处理一个请求,为了弥补这一缺陷就有了Http1.1,Http1.1支持持久连接即一次连接可以处理多个请求,但一个request只能对应一个response,同时Http协议也是一个无状态协议即对于事务处理没有记忆能力,因此需要借助外部机制如Session或Cookie来实现记忆。Http协议建立tcp/ip连接需要进行3次握手,断开连接需要进行4次挥手,同时它只支持单工模式一个request,一个response,response很被动。
Websocket协议:Websocket协议是基于Http协议,建立连接只需要一次握手即可,本身支持持久连接,是一个全双工的通信协议。

三、Websocket能做什么?


Websocket最主要的功能是实现了全双工通信即客户端和服务器都可以充当主动方进行发送数据。

四、示例


框架:Springboot;  (菜鸟教程)
Pom.xml
 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
服务器端

WebSocketConfig
@Configuration
public class WebSocketConfig {
    //检测带有@ServerEndpoint的Bean并注册它们,交给Spring容器进行管理
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
WebSocketServer
@Component
@ServerEndpoint(value="/websocket")
public class WebSocketServer {
    private static Logger logger = Logger.getLogger(WebSocketServer.class);
    public static CopyOnWriteArraySet<WebSocketServer> webSocketServers = new CopyOnWriteArraySet<WebSocketServer>();
    private Session session;
    /**客服端连接的时候触发
     * @throws IOException */
    @OnOpen
    public void onOpen(Session session) throws IOException{
        this.session = session;
        webSocketServers.add(this);
        String message = "{webSocketSessionId:'"+session.getId()+"'}";
        sendMessage(message);
        Map<String, String> map = new HashMap<String,String>();
        map.put("type", "1");
        map.put("content", session.getId());
        sendMessageToAll(map);
        logger.info("sessionID:"+session.getId()+",Open触发");
    }
    /**当客服端断开连接时触发
     * @throws IOException */
    @OnClose
    public void onClose() throws IOException{
        webSocketServers.remove(this);
        Map<String, String> map = new HashMap<String,String>();
        map.put("type", "2");
        map.put("content", this.session.getId());
        sendMessageToAll(map);
        logger.info("sessionID:"+this.session.getId()+",Close触发");
    }
    /**接受客服端发来的信息
     *     
     * */
    @OnMessage
    public void onMessage(String message, Session session) {

    }
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("sessionID:"+session.getId()+",error触发");
    }
    //给当前客户端发送信息
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
    //广播0
    public void sendMessageToAll(Session session) throws IOException{
        for(WebSocketServer wServer:webSocketServers){
            wServer.session.getBasicRemote().sendText("{content:'"+session.getId()+"'}");
        }
    }
    //广播1
    public void sendMessageToAll(String message) throws IOException{
        for(WebSocketServer wServer:webSocketServers){
            wServer.session.getBasicRemote().sendText(message);
        }
    }
    //广播2
    public void sendMessageToAll(Map<String, String>map) throws IOException{
        String message = "";
        if(null != map&& map.size() != 0){
            for(String key:map.keySet()){
                message += key + ":" +"'"+map.get(key)+"',";
            }
        }
        message = message.substring(0,message.length()-1);
        for(WebSocketServer wServer:webSocketServers){
            wServer.session.getBasicRemote().sendText("{"+message+"}");
        }
    }
}
注意:使用websocket时产生的错误
依赖完全正确但是架包找不到,此时需要点击fix project setup添加tomcat8.5版本的架包 。
服务器主动发送消息

    服务器主动发送消息的核心理念就是操作session,通过session发送给指定的客户端。服务器主动发送消息可以发送给指定的用户也可以广播给所有用户,下面是核心代码:
this.session.getBasicRemote().sendText("message");
客户端
//判断当前浏览器是否支持WebSocket
if ("WebSocket" in window){
    var websocket = new WebSocket("ws://localhost:8089/websocket");
    //连接发生错误的回调方法
    websocket.onerror = function(){
        layer.msg("the websocket server is error!");
    };
    //连接成功建立的回调方法
    websocket.onopen = function(event){
        //向服务器主动发送消息
        websocket.send("message");
    }
    //被动获取服务器端传送的数据
    websocket.onmessage = function(event){
        //    转换为json格式
        var json = eval("("+event.data+")");
    }
    //连接关闭的回调方法
    websocket.onclose = function(){
    }
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
}
注意:当页面加载了上述jq代码就已经与服务器建立了连接,离开页面就断开了连接。

三、总结

大部分人使用WebSocket就是看上了它能主动发送消息给客户端,因此知道如何主动发送消息很重要同时这也是最难理解的地方。上述代码中通过调用sendMessage(message);方法来进行主动发送消息,也可以获取到WebSocket的session来进行主动发送消息this.session.getBasicRemote().sendText(“message”);