原创WebSocket--使用/教程/实例
原文网址:WebSocket--使用/教程/实例_IT利刃出鞘的博客-CSDN博客
简介
说明
本文介绍长连接技术:WebSocket。
在线WebSocket测试
websocket/ws/wss在线调试测试工具 (显示页面中等,会打印错误码,可清空, 保留输入)
websocket在线测试 (显示页面大,不打印错误码,不可清空,不保留输入)
http://tool.hibbba.com/websocket/ (显示页面大,不打印错误码,不可清空)
WebSocket介绍
说明
WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)。
首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充。可以通过这样一张图理解:
ajax/长轮询/WebSocket/
ajax轮询:浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
long poll:原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
WebSocket:通过HTTP/HTTPS协议握手后创建一个TCP连接,服务端与客户端通过此TCP连接进行实时通信。
用法
集成方式
SpringBoot有4种集成WebSocket的方式:原生注解,Spring封装,STOMP,TIO。原生注解比较常用,本文使用原生注解。
端口共用
WebSocket与Tomcat共用一个端口。
内置Tomcat与独立Tomcat区别
使用SpringBoot内置Tomcat与使用独立Tomcat配置有所不同。使用SpringBoot内置Tomcat:需要提供一个ServerEndpointExporter的Bean;独立的Tomcat容器:不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。
单例与多个Bean
虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
示例
代码
依赖
implementation 'org.springframework.boot:spring-boot-starter-websocket'
spring-boot-starter-websocket内部引入了spring-boot-starter-web 。
配置
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
发送/接收
java
package com.example.demo.common;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{token}")
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//保存客户端所对应的WebSocketServer
private static Map<String, WebSocketServer> clientMap = new ConcurrentHashMap<>();
private String token;
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 连接建立成功调用的方法
@OnOpen
public void onOpen(@PathParam("token") String token, Session session) {
//TODO 校验token
this.session = session;
addOnlineCount();
clientMap.put(token, this);
log.info("新连接加入!" + " token:" + token + "; session.getId():" + session.getId() + " 当前连接数:" + onlineCount);
}
// 连接关闭
@OnClose
public void onClose() {
subOnlineCount();
clientMap.remove(token);
log.info("有一连接关闭,当前连接数为:" + onlineCount);
}
// 收到客户端消息
@OnMessage
public void onMessage(String message, Session session) throws IOException {
log.info("来自客户端的消息:" + message);
sendMsgToAll(message);
}
// 发生错误
@OnError
public void onError(Session session, Throwable error) {
log.info("发生错误!");
error.printStackTrace();
}
public void sendMessage(String token, String message) throws IOException {
if (!StringUtils.isEmpty(token) && clientMap.containsKey(token)) {
clientMap.get(token).send(message);
log.info("成功发送一条消息:" + message);
} else {
log.error("用户:" + token + ",不在线!");
}
}
public void send(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
}
// 给所有客户端群发消息
public void sendMsgToAll(String message) throws IOException {
for (WebSocketServer item : clientMap.values()) {
item.session.getBasicRemote().sendText(message);
}
log.info("成功群发一条消息:" + onlineCount);
}
public static synchronized int getOnlineCount() {
return WebSocketServer.onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
控制器
java
package com.example.demo.controller;
import com.example.demo.common.WebSocketServer;
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;
@RestController
@RequestMapping("")
public class HelloController {
@Autowired
WebSocketServer webSocketServer;
@PostMapping("send")
void send(String token, String message) {
try {
webSocketServer.sendMessage(token, message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试
前端连接
在线网址:websocket在线测试 输入连接地址:ws://localhost:8080/ws/abc
![图片]:(https://img-blog.csdnimg.cn/20201029185422618.png)
后端结果
新连接加入! token:abc; session.getId():0 当前连接数:1
后端发送给前端
postman访问:http://localhost:8080/send?message=Hello World&token=abc
后端结果
成功发送一条消息:Hello World
在线网址结果
![图片]:(https://img-blog.csdnimg.cn/20201029185442781.png)
前端发送给后端
![图片]:(https://img-blog.csdnimg.cn/20201029185535667.png?
x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc=,size_16,color_FFFFFF,t_70)
后端结果
来自客户端的消息:Welcome
成功群发一条消息:1
前端结果
![图片]:(https://img-blog.csdnimg.cn/20201029185646737.png)
关闭后端程序
后端结果
发生错误!
前端结果
![图片]:(https://img-blog.csdnimg.cn/202010291857302.png)
其他网址
SpringBoot2.0集成WebSocket,实现后台向前端推送信息_★【World Of Moshow 郑锴】★-CSDN博客
Spring Boot 使用 WebSocket 实现消息推送 及 WebSocket原理_二一点-CSDN博客
===========================
【来源: CSDN】
【作者: IT利刃出鞘】
【原文链接】 https://knife.blog.csdn.net/article/details/108103610
声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢。