WebSocket原理

2022-12-05  本文已影响0人  坤坤坤坤杨

WebSocket是应用层上的一个应用层协议,持久化的协议,他必须依赖http协议进行一次握手,成功后就直接从tcp通道传输,后续就与http无关了。

WebSocket是以frame形式传输数据的,比如会将一条消息分为多个frame,按照先后顺序传输出去,他的好处有以下几点:

  1. 大数据的传输可以分片传输,不用考虑到数据大小导致的长度标志不足的请求。
  2. 和http的chunk一样,可以一边产生数据,一边传递数据,即提高传输效率。

1. 特点

  1. 长连接。
  2. 主要是服务端给客户端发送数据。
  3. WebSocket使用时浏览器与服务端最开始建立的还是http连接,之后再将协议转换为ws,在请求头上增加额外信息 upgrade:webSocket,connection:upgrade,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==  //是一个 Base64 encode 的值,这个是浏览器随机生成的
    Sec-WebSocket-Protocol: chat, superchat //是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议
    Sec-WebSocket-Version: 13 //告诉服务器所使用的 WebSocket Draft (协议版本)
    Origin: http://example.com
    
  4. 端口与http协议一样,默认80,wss和https一样,默认443
  5. 减少了http长轮询造成的资源浪费,解决了不能由服务端向客户端主动推送的问题,提高了实时性。
  6. 数据格式比较轻量,性能开销小,通信高效。
  7. 可以发送文本,也可以发送二进制数据。
  8. 没有同源限制,客户端可以与任意服务器通信。
  9. 建立在 TCP 协议之上,服务器端的实现比较容易。

2. 在WebSocket之前,web上实现实时数据交互的方式

  1. 定期轮询:客户端按照某个时间间隔不断地向服务器发送请求,请求服务端的最新数据然后更新数据到客户端显示,这种方式浪费了大量的流量,并且对服务端造成了很大的压力。
  2. 基于长轮询的服务端推送技术:客户端首先给服务端发送一个请求,服务端收到该请求之后如果数据没有更新则并不立刻返回,服务端会阻塞请求的放回,直到数据发生了更新或者发生了连接超时,服务端返回数据之后客户端再次发送同样的请求。

3. 服务端代码实现(tomcat)

package com.scoket.scoketTest;


import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/websocket")
public class WebSocketTest {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
        //群发消息
        for(WebSocketTest item: webSocketSet){
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketTest.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketTest.onlineCount--;
    }
}
上一篇下一篇

猜你喜欢

热点阅读