WebSocket

1.定义

        WebSocket 是HTML5开始提供的一种基于TCP的浏览器与服务器间进行全双工通讯的网络技术,相对于 HTTP 这种非持久的协议来说,WebSocket 是一个持久化网络通信的协议。依靠这种技术可以实现客户端和服务器端的长连接双向实时通信

2.特征

  1. 建立在 TCP 协议之上;
  2. 默认端口也是80和443,并且握手阶段采用 HTTP 协议;
  3. 数据格式比较轻量,性能开销小,通信高效;
  4. 可以发送文本,也可以发送二进制数据;
  5. 没有同源限制,客户端可以与任意服务器通信;
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

ws://xxx.ayunw.cn:80/some/path
wss://xxx.ayunw.cn:443/some/path

3.逻辑图

在这里插入图片描述

 4.主要部分

  1. 握手
    1. 来自客户端的握手看起来像如下形式:
      GET ws://localhost/chat HTTP/1.1
      Host: localhost
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
      Sec-WebSocket-Protocol: chat,superchat
      Sec-WebSocket-Version: 13
      
    2. 来自服务器的握手看起来像如下形式:
      HTTP/1.1 101 Switching Protocols
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
      Sec-WebSocket-Protocol: chat
      
  2. 数据传输

        服务端

  1. MAVEN依赖
       <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
    
  2.  WebSocket配置类
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    @Configuration
    public class WebSocketConfig {
        /**
         * 	注入ServerEndpointExporter,
         * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
        
    }
    
  3. WebSocket操作类

    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    
    import org.springframework.stereotype.Component;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Component
    @Slf4j
    @ServerEndpoint("/websocket/{userId}")  // 接口路径 ws://localhost:8087/webSocket/userId;
    
    public class WebSocket {
        
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
            /**
         * 用户ID
         */
        private String userId;
        
        //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
        //虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
        //  注:底下WebSocket是当前类名
        private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();
        // 用来存在线连接用户信息
        private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();
        
        /**
         * 链接成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam(value="userId")String userId) {
            try {
    			this.session = session;
    			this.userId = userId;
    			webSockets.add(this);
    			sessionPool.put(userId, session);
    			log.info("【websocket消息】有新的连接,总数为:"+webSockets.size());
    		} catch (Exception e) {
    		}
        }
        
        /**
         * 链接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            try {
    			webSockets.remove(this);
    			sessionPool.remove(this.userId);
    			log.info("【websocket消息】连接断开,总数为:"+webSockets.size());
    		} catch (Exception e) {
    		}
        }
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message
         * @param session
         */
        @OnMessage
        public void onMessage(String message) {
        	log.info("【websocket消息】收到客户端消息:"+message);
        }
        
    	  /** 发送错误时的处理
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
    
            log.error("用户错误,原因:"+error.getMessage());
            error.printStackTrace();
        }
    
        
        // 此为广播消息
        public void sendAllMessage(String message) {
        	log.info("【websocket消息】广播消息:"+message);
            for(WebSocket webSocket : webSockets) {
                try {
                	if(webSocket.session.isOpen()) {
                		webSocket.session.getAsyncRemote().sendText(message);
                	}
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        // 此为单点消息
        public void sendOneMessage(String userId, String message) {
            Session session = sessionPool.get(userId);
            if (session != null&&session.isOpen()) {
                try {
                	log.info("【websocket消息】 单点消息:"+message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        // 此为单点消息(多人)
        public void sendMoreMessage(String[] userIds, String message) {
        	for(String userId:userIds) {
        		Session session = sessionPool.get(userId);
                if (session != null&&session.isOpen()) {
                    try {
                    	log.info("【websocket消息】 单点消息:"+message);
                        session.getAsyncRemote().sendText(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
        	}
            
        }
        
    }
    
  4. 调用实例

    @Resource
    private WebSocket webSocket;
    
    
    //创建业务消息信息
    JSONObject obj = new JSONObject();
    obj.put("cmd", "topic");//业务类型
    obj.put("msgId", sysAnnouncement.getId());//消息id
    obj.put("msgTxt", sysAnnouncement.getTitile());//消息内容
    //全体发送
    webSocket.sendAllMessage(obj.toJSONString());		
    //单个用户发送 (userId为用户id)
    webSocket.sendOneMessage(userId, obj.toJSONString());		
    //多个用户发送 (userIds为多个用户id,逗号‘,’分隔)
    webSocket.sendMoreMessage(userIds, obj.toJSONString());
    

客户端

<script>
    import store from '@/store/'

    export default {
        data() {
            return {
            }
        },
        mounted() { 
              //初始化websocket
              this.initWebSocket()
        },
        destroyed: function () { // 离开页面生命周期函数
              this.websocketclose();
        },
        methods: {
            initWebSocket: function () { // 建立连接
                // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
                var userId = store.getters.userInfo.id;
                var url = window._CONFIG['domianURL'].replace("https://","wss://").replace("http://","ws://")+"/websocket/"+userId;
                this.websock = new WebSocket(url);
                this.websock.onopen = this.websocketonopen;
                this.websock.send = this.websocketsend;
                this.websock.onerror = this.websocketonerror;
                this.websock.onmessage = this.websocketonmessage;
                this.websock.onclose = this.websocketclose;
              },
              // 连接成功后调用
              websocketonopen: function () {
                console.log("WebSocket连接成功");
              },
              // 发生错误时调用
              websocketonerror: function (e) {
                console.log("WebSocket连接发生错误");
              },
              // 给后端发消息时调用
              websocketsend: function (e) {
                console.log("WebSocket连接发生错误");
              },
							// 接收后端消息
              // vue 客户端根据返回的cmd类型处理不同的业务响应
              websocketonmessage: function (e) {
                var data = eval("(" + e.data + ")"); 
                 //处理订阅信息
                if(data.cmd == "topic"){
                   //TODO 系统通知
             
                }else if(data.cmd == "user"){
                   //TODO 用户消息
        
                }
              },
              // 关闭连接时调用
              websocketclose: function (e) {
                console.log("connection closed (" + e.code + ")");
              }
        }
    }
</script>

 5.方法解释

在这里插入图片描述

6.接口调用顺序 

  1. 进来页面 : 先建立连接–》调用websocketonopen方法,链接成功调用的方法
  2. websocketonmessage方法为接收后端时处理。
  3. 当我们要发送消息给后端时调用websocketsend。
  4. 当我们要关闭连接时调用websocketclose。
  5. 当发现错误时调用websocketonerror。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值