WebSocket协议介绍与使用

WebSocket协议

    WebSocket协议是一种网络通信协议,是HTML5之后提出的一种在单个TCP上进行全双工通讯的协议。可以使服务端主动的向客户端发送消息,常用于推送服务。其特点如下:

  • HTTP协议(1.0)是单向的没有连接状态的协议,客户端浏览器为了获取服务器的动态变化信息,需要配合JavaScript和AJAX进行不断的异步轮询请求,需要不断的建立连接,非常耗费资源。

  • WebSocket能够使任一方主动的建立起连接,并且连接只要建立一次就一直保持连接状态。

    WebSocket并不是全新的协议,而是利用HTTP协议来建立连接的,其创建过程如下:

  1. 浏览器发送HTTP请求
 GET ws://localhost:3000/hello HTTP/1.1
 Host: localhost
 Upgrade: websocket
 Connection: Upgrade
 Origin: http://localhost:3000
 Sec-WebSocket-Key: client-random-string
 Sec-WebSocket-Version: 13

说明:

  • GET请求的地址不是类似/path/,而是以ws://开头的地址;

  • 请求头Upgrade: websocketConnection: Upgrade表示这个连接将要被转换为WebSocket连接;

  • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;

  • Sec-WebSocket-Version指定了WebSocket的协议版本。

  1. 服务器接受该请求,就会返回下面的响应:
 HTTP/1.1 101 Switching Protocols
 Upgrade: websocket
 Connection: Upgrade
 Sec-WebSocket-Accept: server-random-string

    该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。

  1. WebSocket连接建立,服务器端可以主动的发送消息给浏览器,可以是二进制数据和文本数据。

    
下面是如何在前端分离的项目中使用WebSocket协议,前端Vue,后端Spring Boot。

WebSocket客户端

适用于支持html5的浏览器,其开放的API(不需要引入)为:

  1. 创建Web Socket连接
 var Socket = new WebSocket(url, [protocol] );

url表示指定连接的URL,protocol是子协议,可选。

对象属性:

 Socket.readyState: 连接状态,0表示连接未建立;1表示连接建立,可以进行通信;2表示连接正在关闭;3表示连接已经关闭,并且不能打开。
 Socket.bufferAmount:等待传输的队列。

对象事件:

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage浏览器客户端接收服务器的信息时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

对象方法:

  • Socket.send():使用连接发送数据。

  • Socket.close():关闭连接。

 // 初始化一个 WebSocket 对象
 var socket = new WebSocket('ws://localhost:9998/echo');// 建立 web socket 连接成功触发事件
 socket.onopen = function() {
   // 使用 send() 方法发送数据
   socket.send('发送数据');
   alert('数据发送中...');
 };// 接收服务端数据时触发事件
 socket.onmessage = function(evt) {
   var received_msg = evt.data;
   alert('数据已接收...');
 };// 断开 web socket 连接成功触发事件
 socket.onclose = function() {
   alert('连接已关闭...');
 };

    

Vue使用web socket

 // 建立web socket连接
 webSocket() {
     if ('WebSocket' in window) {
     this.websocket = new WebSocket("ws://localhost:8081/websocket/hello");
     } else {
     console.log("你的浏览器还不支持web socket");
 }
 console.log(this.websocket);
 this.websocket.onopen = this.webSocketOnOpen;
 this.websocket.onclose = this.webSocketOnClose;
 this.websocket.onmessage = this.webSocketOnMessage;
 this.websocket.onerror = this.webSocketOnError;
 },// 连接建立之后
 webSocketOnOpen() {
     console.log("与后端建立连接");
     var data = "这里是端口8001建立连接";
     this.websocket.send(data);
 },// 连接关闭之后
 webSocketOnClose() {
     console.log("与后端关闭连接");
 },// 发送消息
 webSocketOnMessage(res) {
     console.log("接收到后端发送回来的消息");
     console.log(res.data)
 },// 发生错误
 webSocketOnError() {
     console.log("发生错误~");
 },

使用原生的web socket就行了,也有封装了web socket的工具可以使用。

    

WebSocket服务端

如果是使用Tomcat服务器的话,要版本7以上才支持,使用Spring Boot整合WebSocket的过程如下:

  1. 引入依赖
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
 </dependency>
  1. 创建核心配置类
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.messaging.simp.config.MessageBrokerRegistry;
 import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
 import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
 import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
 import org.springframework.web.socket.server.standard.ServerEndpointExporter;/**
  * spring boot web socket 核心配置类
  */
 @Configuration
 public class WebSocketConfig {
     /**
      * 配置服务端点导出器
      * @return
      */
     @Bean
     public ServerEndpointExporter serverEndpointExporter() {
         return new ServerEndpointExporter();
     }}
  1. 创建服务端点
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.RequestParam;import javax.websocket.*;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
 import java.io.IOException;
 import java.util.Map;
 import java.util.concurrent.CopyOnWriteArraySet;/**
  * 后端实现web socket
  */
 @Component
 @ServerEndpoint("/hello")
 @Slf4j
 public class WebSocketServer {
     /**
      * 静态变量,用来记录当前在线连接数
      * 注意线程安全问题
      */
     private static int onlineCount = 0;
     
     /**
      * concurrent包的线程安全集合,存放每个客户端对应的web socket对象
      */
     private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();/**
      * 与某个客户端的连接会话,需要听过它来给客户端发送数据
      * 每个前端的WebSocket对象建立起来的有一个session对象
      */
     private Session session;/**
      * 连接建立成功调用的方法
      */
     @OnOpen
     public void onOpen(Session session) {
         this.session = session;
         System.out.println("session = " + session);
         // 添加当前web socket对象
         webSocketSet.add(this);
         // 累加在线人数
         addOnlineCount();
         log.info("有新连接加入!当前在线人数为" + getOnlineCount());
         try {
             sendMessage("有新连接加入!当前在线人数为" + getOnlineCount());
         } catch (IOException e) {
             log.error("websocket IO异常");
         }
     }/**
      * 连接关闭调用的方法
      */
     @OnClose
     public void onClose() {
         // 将连接的web socket对象从set中移除
         webSocketSet.remove(this);
         // 在线人数-1
         subOnlineCount();
         log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
     }/**
      * 收到客户端消息后调用的方法
      * @param message 客户端发送过来的消息
      */
     @OnMessage
     public void onMessage(String message, Session session) {
         log.info("来自客户端的消息:" + message);//群发消息
         for (WebSocketServer item : webSocketSet) {
             try {
                 item.sendMessage(message);
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }/**
      * 发生错误
      * @param session
      * @param error
      */
     @OnError
     public void onError(Session session, Throwable error) {
         log.error("发生错误");
         error.printStackTrace();
     }/**
      * 给客户端浏览器发送消息
      * @param message
      * @throws IOException
      */
     public void sendMessage(String message) throws IOException {
         this.session.getBasicRemote().sendText(message);
     }
     /**
      * 群发自定义消息
      * */
     public static void sendInfo(String message) throws IOException {
         log.info(message);
         for (WebSocketServer item : webSocketSet) {
             try {
                 item.sendMessage(message);
             } catch (IOException e) {
                 continue;
             }
         }
     }
     public static synchronized int getOnlineCount() {
         return onlineCount;
     }
     public static synchronized void addOnlineCount() {
         WebSocketServer.onlineCount++;
     }
     public static synchronized void subOnlineCount() {
         WebSocketServer.onlineCount--;
     }
 }

如果要获取前端发送的消息可用:

 @Component
 @ServerEndPoint("/hello/{name}/{age}")
 public class WebSocketServer {
     @OnOpen
     public void onOpen(@PathParam("name")String name, @PathParam("age")int age) {
         
     }
 }

    
【参考】廖雪峰的官方网站https://www.liaoxuefeng.com/wiki/1022910821149312/1103303693824096

    
    

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值