WebSocket

1、什么是webSocket?2、webSocket可以用来做什么?3、webSocket协议4、服务端WebSocket配置类WebSocket操作类

5、webSocket网页客户端工具通讯测试

1、什么是webSocket?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

2、webSocket可以用来做什么?

利用双向数据传输的特点可以用来完成很多功能,不需要前端轮询,浪费资源。例如:

1、通告功能 2、聊天功能 (如下是逻辑图) 3、实时更新数据功能 4、弹幕

3、webSocket协议

本协议有两部分:握手和数据传输。 握手是基于http协议的。

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

来自服务器的握手看起来像如下形式:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

4、服务端

maven依赖

org.springframework.boot

spring-boot-starter-websocket

cn.hutool

hutool-core

5.7.22

WebSocket配置类

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**

* @author Hao

* @date 2023-03-01 16:18

* @description:

* @version:

*/

@Configuration

public class WebSocketConfig {

/**

* 注入ServerEndpointExporter,

* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint

*/

@Bean

public ServerEndpointExporter serverEndpointExporter() {

return new ServerEndpointExporter();

}

}

WebSocket操作类

通过该类WebSocket可以进行群推送以及单点推送

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

import org.springframework.stereotype.Component;

import cn.hutool.core.util.StrUtil;

import javax.websocket.*;

import javax.websocket.server.PathParam;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;

import java.util.concurrent.ConcurrentHashMap;

/**

* @author Hao

* @date 2023-03-01 16:18

* @description:

* @version:

*/

@ServerEndpoint(value = "/websocket/{userId}")

@Component

public class WebSocket {

private final static Logger logger = LogManager.getLogger(WebSocket.class);

/**

* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的

*/

private static int onlineCount = 0;

/**

* concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象

*/

private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap<>();

/**

* 与某个客户端的连接会话,需要通过它来给客户端发送数据

*/

private Session session;

private String userId;

/**

* 连接建立成功调用的方法

*/

@OnOpen

public void onOpen(Session session, @PathParam("userId") String userId) {

this.session = session;

this.userId = userId;

//加入map

webSocketMap.put(userId, this);

addOnlineCount(); //在线数加1

logger.info("用户{}连接成功,当前在线人数为{}", userId, getOnlineCount());

try {

sendMessage(String.valueOf(this.session.getQueryString()));

} catch (IOException e) {

logger.error("IO异常");

}

}

/**

* 连接关闭调用的方法

*/

@OnClose

public void onClose() {

//从map中删除

webSocketMap.remove(userId);

subOnlineCount(); //在线数减1

logger.info("用户{}关闭连接!当前在线人数为{}", userId, getOnlineCount());

}

/**

* 收到客户端消息后调用的方法

*

* @param message 客户端发送过来的消息

*/

@OnMessage

public void onMessage(String message, Session session) {

logger.info("来自客户端用户:{} 消息:{}",userId, message);

//群发消息

/*for (String item : webSocketMap.keySet()) {

try {

webSocketMap.get(item).sendMessage(message);

} catch (IOException e) {

e.printStackTrace();

}

}*/

}

/**

* 发生错误时调用

*

* @OnError

*/

@OnError

public void onError(Session session, Throwable error) {

logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());

error.printStackTrace();

}

/**

* 向客户端发送消息

*/

public void sendMessage(String message) throws IOException {

this.session.getBasicRemote().sendText(message);

//this.session.getAsyncRemote().sendText(message);

}

/**

* 通过userId向客户端发送消息

*/

public void sendMessageByUserId(String userId, String message) throws IOException {

logger.info("服务端发送消息到{},消息:{}",userId,message);

if(StrUtil.isNotBlank(userId)&&webSocketMap.containsKey(userId)){

webSocketMap.get(userId).sendMessage(message);

}else{

logger.error("用户{}不在线",userId);

}

}

/**

* 群发自定义消息

*/

public static void sendInfo(String message) throws IOException {

for (String item : webSocketMap.keySet()) {

try {

webSocketMap.get(item).sendMessage(message);

} catch (IOException e) {

continue;

}

}

}

public static synchronized int getOnlineCount() {

return onlineCount;

}

public static synchronized void addOnlineCount() {

WebSocket.onlineCount++;

}

public static synchronized void subOnlineCount() {

WebSocket.onlineCount--;

}

}

测试controller

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**

* @author Hao

* @date 2023-03-01 16:18

* @description:

* @version:

*/

@RestController

@RequestMapping("/webSocket")

public class WebSocketController {

@Autowired

private WebSocket webSocket;

@PostMapping("/sentMessage")

public void sentMessage(String userId,String message){

try {

webSocket.sendMessageByUserId(userId,message);

} catch (IOException e) {

e.printStackTrace();

}

}

}

5、webSocket网页客户端工具

http://websocket.jsonin.com/

复制三个tab

WebSocket地址分别输入后连接

ws://127.0.0.1:8051/websocket/1

ws://127.0.0.1:8051/websocket/2

ws://127.0.0.1:8051/websocket/3

通讯测试

1.往客户端发消息

127.0.0.1:8092/webSocket/sentMessage?userId=1&message=请进入视频会议

127.0.0.1:8092/webSocket/sentMessage?userId=2&message=请进入视频会议

127.0.0.1:8092/webSocket/sentMessage?userId=3&message=请进入视频会议

2.客户端收到消息回复“好的”

最后送所有正在努力的大家一句话:

你不一定逆风翻盘,但一定要向阳而生。

期待下次发布好的文章:

山水相逢,我们江湖见。

查看原文