❝WebSocket是一种在单个TCP连接上进行全双工通信的协议。
❞
介绍
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
背景
很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。
假如轮询的时间间隔是1秒,由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。
这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
优点
-
实时性:服务器可以随时主动给客户端下发数据,相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少。
-
长连接:Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
使用
引入Maven依赖
在pom.xml
文件中dependencies
标签里加入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建WebSocket配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* ---------------------------
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
* ---------------------------
* @author:XiaoYang
* @date:2020-12-18
* ---------------------------
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocket 服务端
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* ---------------------------
* WebSocket 服务端 Demo
* ---------------------------
* @author:XiaoYang
* @date:2020-12-18
* ---------------------------
*/
@ServerEndpoint("/webSocket/demo")
@Component
@Slf4j
public class DemoWebSocket {
/**
* 存放所有在线的客户端
*/
private static Map<String, Session> clients = new ConcurrentHashMap<>();
/**
* 客户端连接建立成功后调用的方法
* @param session session
*/
@OnOpen
public void onOpen(Session session) {
clients.put(session.getId(), session);
log.info("有新的客户端连接了, id为: {},当前在线客户端数量:{}", session.getId(), clients.size());
}
/**
* 客户端关闭后调用的方法
* @param session session
*/
@OnClose
public void onClose(Session session) {
clients.remove(session.getId());
log.info("有客户端关闭, id为:{},当前在线客户端数量:{}", session.getId(), clients.size());
}
/**
* 发生错误时调用的方法
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("WebSocket发生错误", error);
}
/**
* 收到客户端消息后调用的方法
* @param message 消息内容
* @param session
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("来自客户端的消息:{}", message);
// 群发
this.sendAll(message);
}
/**
* 向所有客户端发送消息
* @param message 消息内容
*/
private void sendAll(String message) {
for(Map.Entry<String, Session> sessionEntry: clients.entrySet()) {
Session session = sessionEntry.getValue();
synchronized (session) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("WebSocket发送消息时发生IO异常", e);
}
}
}
}
}
测试
启动项目后,我们使用在线WebSocket测试网站进行测试。打开http://www.jsons.cn/websocket/
网站。
输入WebSocket测试地址,连接格式为 ws://IP或域名:端口+路径,我们这里是ws://127.0.0.1:8100/webSocket/demo
。
点击连接按钮:
发送消息测试:
控制台输出如下:
有新的客户端连接了, id为: 3,当前在线客户端数量:3
来自客户端的消息:123456
来自客户端的消息:654321
浏览器可以多开几个窗口连接测试。
学习记录的公众号,欢迎大家关注: