websocket

websocket是一种网络通信协议。

全双工。

弊端:HTTP协议无法实现服务器主动向客户端发起消息,

握手阶段

握手是基于http协议的

请求头

websocket_第1张图片

请求头说明

Connection:Upgrade   协议升级为WebSocket协议

Upgrade:WebSocket    标识该HTTP请求是一个协议升级请求

​HTTP1.1 协议规定,Upgrade表示将通信协议从HTTP/1.1转向该字段指定的协议

Sec-Websocket-Key则是用于握手协议的密钥。

客户端采用base64编码的24位随机字符序列,服务器接受客户端HTTP协议升级的证明。
要求服务端响应一个对应加密的Sec-WebSocket-Accept头信息作为应答

Sec-WebSocket-Extensions: 协议扩展类型 

响应头

websocket_第2张图片

Sec-WebSocket-Accept:浏览器对这个值进行验证,以证明确实是目标服务器回应了 WebSocket 请求。


数据交互阶段

前端环境搭建

使用vscode,搭建咯

客户端创建WebSocket

浏览器端创建一个WebSocket

//参数url格式: ws://ip地址:端口号/资源名称
var ws = new WebSocket(url);

websocket_第3张图片


给websocket绑定回调函数

websocket_第4张图片

//onopen onopen

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});


ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});


ws.addEventListener("message", function(event) {
  
 //文本数据
  if(typeOf event.data === String) {
    console.log("Received data string");
  }
  var data = event.data;
  // 处理数据
});


ws.addEventListener("error", function(event) {
  // handle error event
});

send()发送数据

 websocket方法

ws.send('your message');

webSocket.readyState

readyState属性返回实例对象的当前状态,共有四种。

  • CONNECTING:值为0,表示正在连接。
  • OPEN:值为1,表示连接成功,可以通信了。
  • CLOSING:值为2,表示连接正在关闭。
  • CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

服务端

1、依赖引入


    org.springframework.boot
    spring-boot-starter-websocket

2、配置类

注入使用@ServerEndpoint的Websocket

@Configuration
public class WebSocketConfig {
    /**
     * 	注入ServerEndpointExporter,
     * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

Tomcat7.0.5支持 websocket

EndPoint对象,对应唯一的一个客户端。类比为Servlet

每一个连接,都会创建一个@ServerEndpoint 标注的类的实例。

websocket_第5张图片

@ServerEndpoint (”/资源路径“)

websocket_第6张图片

websocket_第7张图片

但是光写@Component还不够,还需要在配置类中注入

@Configuration
public class WebSocketConfig {
    /**
     * 注入ServerEndpointExporter,
     * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}


3、三个属性的必要性 

服务端接收客户端消息

通过@OnMessage注解指定接收消息的方法

服务端发送数据给客户端

发送消息则由RemoteEndpoint完成,其实例由session维护,根据使用情况,

我们可以通过

1、session.getBasicRemote获取同步消息发送的实例,然后调用其 sendXxx()方法就可以发送消息

2、可以通过session.getAsyncRemote获取异步消息发送实例

session.getBasicRemote().sendText(msg);


4、消息格式

websocket_第8张图片

websocket_第9张图片

websocket_第10张图片

5、websocket测试

websocket在线测试

websocket_第11张图片

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
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 {

    //存储的消息内容
    public static ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(100);

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

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    //虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
    //注:底下WebSocket是当前类名
    private static CopyOnWriteArraySet webSockets = new CopyOnWriteArraySet<>();
    // 用来存在线连接用户信息
    private static ConcurrentHashMap sessionPool = new ConcurrentHashMap();

    /**
     * 链接成功调用的方法
     */
    @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());
            //向客户端发送连接成功
            sendOneMessage(userId, "建立连接成功");
        } 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
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端消息:" + message);
        //发送队列中的内容
        if (!arrayBlockingQueue.isEmpty() && message.equals("1")) {//在请求数据
            String msa = (String) arrayBlockingQueue.element();
            sendOneMessage(userId, msa);
            Object remove = arrayBlockingQueue.remove();//移除成功返回移除的数据
        }
    }

    /**
     * 发送错误时的处理
     *
     * @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();
                }
            }
        }

    }

}

你可能感兴趣的:(websocket,网络协议,网络)