webScoket的简单使用demo讲解
(1) webSocket定义
WebSocket是一种在单个TCP连接上进行全双工通信
的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性
的连接,并进行双向数据传输
。
(2)再了解一下什么是长连接和短链接(可以提前了解一下什么是三次握手和四次挥手)
长连接
长连接,也叫持久连接
,在TCP层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息(包括心跳)交互,直至连接的任意一方(客户端OR服务端)主动断开连接,此过程称为一次完整的长连接。
短连接
短连接,顾名思义,与长连接的区别就是,客户端收到服务端的响应后,立刻发送FIN消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互(发请求-收响应)
之后立刻断开
连接的情况都称为短连接。
1.什么时候用长连接,短连接?
1、需要频繁交互
的场景使用长连接
,如即时通信工具(微信/QQ
,QQ也有UDP),相反则使用短连接,比如普通的web网站,只有当浏览器发起请求时才会建立连接,服务器返回相应后,连接立即断开。
2、维持长连接会有一定的系统开销,用户量少不容易看出系统瓶颈,一旦用户量上去了,就很有可能把服务器资源(内存/CPU/网卡)耗尽,所以使用需谨慎。
(3) WebSocket 方法和事件
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
(4)Spring整合webSocket
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
(1)首先WebSocketConfiguration
@Configuration
@EnableWebSocket
public class WebSocketConfiguration {
//发送消息的大小限制
private static final int MAX_MESSAGE_SIZE = 100 * 100 * 1024;
//session空闲会话超时设置
private static final long MAX_IDLE = 60 * 60 * 1000;
/**
* ServerEndPointExporter,在springboot内置容器(嵌入式容器)中运行时,必须上下文提供ServerEndpointExporter
* 但是在tomcat容器中运行时,扫描工作会交给容器处理,不需要bean注入,因此下面这段代码旨在开发库中存在,并没有合并提交的过程库
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
@Bean
public ServletServerContainerFactoryBean createServletServerContainerFactoryBean() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(MAX_MESSAGE_SIZE);
container.setMaxBinaryMessageBufferSize(MAX_MESSAGE_SIZE);
container.setMaxSessionIdleTimeout(MAX_IDLE);
return container;
}
}
(2)首先webSocketService demo
@ServerEndpoint("/app/websocket/server")
@Component
public class WebsocketServer {
StartService startService = SpringContext.getBean("startService", StartService.class);
private static ZSmartLogger logger = ZSmartLogger.getLogger(WebsocketServer.class);
@OnMessage
public void onMessage(String message, Session session) throws IOException {
logger.info("onMessage: {}", message);
message = StringUtils.trim(message);
if (StringUtils.isEmpty(message)) {
return;
}
else if ("ping".equalsIgnoreCase(message)) {
try {
session.getBasicRemote().sendText("pong");
}
catch (Exception e) {
logger.warn("Send pong error, e: {}", e.getMessage());
}
}
else {
session.getBasicRemote().sendText("Received your message-{" + message + "}, please go on!");
}
startService.storeMessage(message);
}
@OnOpen
public void onOpen(Session session) {
String flowNo = getFlowNo(session);
if (StringUtils.isNotEmpty(flowNo)) {
startService.setSession(flowNo, session);
}
logger.info("Client connected, flowNo: {}.", flowNo);
}
@OnClose
public void onClose(Session session) {
String flowNo = getFlowNo(session);
if (StringUtils.isNotEmpty(flowNo)) {
startService.removeSession(flowNo);
}
logger.info("WebsocketServer.onClose start...Connection closed");
}
@OnError
public void onError(Session session, Throwable error) {
String flowNo = getFlowNo(session);
if (StringUtils.isNotEmpty(flowNo)) {
startService.removeSession(flowNo);
}
logger.warn("WebsocketServer on error, exception = {}.", error);
}
private String getFlowNo(Session session) {
Map<String, List<String>> requestParams = session.getRequestParameterMap();
List<String> flowNoArry = requestParams.get("flowNo");
String flowNo = null;
if (CollectionUtils.isNotEmpty(flowNoArry)) {
flowNo = flowNoArry.get(0);
}
return flowNo;
}
}
(3)start Service 先将session设置到内存,断开连接的时候从内存中删除
@Component
public class StartService {
private static Map<String, Session> map = new HashMap<>();
private static List<Map<String, String>> list = new ArrayList<>();
private static ZSmartLogger logger = ZSmartLogger.getLogger(StartService.class);
public void setSession(String flowNo, Session session) {
logger.info("setSession, flowNo: {}.", flowNo);
map.put(flowNo, session);
}
public void removeSession(String flowNo) {
map.remove(flowNo);
}
public void start() throws IOException {
logger.info("start.");
for (Map.Entry<String, Session> entrySet: map.entrySet()) {
Session session = entrySet.getValue();
session.getBasicRemote().sendText("start");
logger.info("start flowNo: {}, value: {}.", entrySet.getKey(), entrySet.getValue());
}
}
public void dispatch() throws IOException {
logger.info("dispatch.");
for (Map.Entry<String, Session> entrySet: map.entrySet()) {
Session session = entrySet.getValue();
session.getBasicRemote().sendText("dispatch");
logger.info("dispatch flowNo: {}, value: {}.", entrySet.getKey(), entrySet.getValue());
}
}
public void storeMessage(String message) {
logger.info("storeMessage, message-{}.", message);
Map<String, String> msgMap = new HashMap<>();
if (message.startsWith("data:")) {
msgMap.put("type", "image");
}
else {
msgMap.put("type", "text");
}
msgMap.put("content", message);
list.add(msgMap);
}
public List<Map<String, String>> fetchMessage() {
logger.info("fetchMessage.");
List<Map<String, String>> newList = new ArrayList(list);
list.clear();
return newList;
}
}
如果有问题欢迎指正!