目录
Web Sockets的目标是在一个单独的持久连接上提供全双工、双向通信。在JavaScript中创建了WebSocket之后。会有一个HTTP请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会从HTTP升级为Web Socket协议(ws)。

我们想要查询当前的排队情况,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)
websocket的优点:
1.支持双向通信,实时性更强
2.更好的二进制支持
3.较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较
小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户
端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头
部。
4.支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定
义压缩算法等)
总结就是::支持双向通信,更灵活,更高效,可扩展性更好。
- <!--webSocket-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-websocket</artifactId>
- </dependency>
- /**
- * websocket
- * 的配置信息
- */
- @Configuration
- public class WebSocketConfig {
-
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
-
- return new ServerEndpointExporter();
- }
- }
- /**
- * websocket的处理类。
- * 作用相当于HTTP请求
- * 中的controller
- */
- @Component
- @Slf4j
- @ServerEndpoint("/api/pushMessage/{userId}")
- public class WebSocketServer {
-
- /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
- private static int onlineCount = 0;
- /**concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/
- private static ConcurrentHashMap
webSocketMap = new ConcurrentHashMap<>(); - /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
- private Session session;
- /**接收userId*/
- private String userId = "";
-
- /**
- * 连接建立成
- * 功调用的方法
- */
- @OnOpen
- public void onOpen(Session session,@PathParam("userId") String userId) {
- this.session = session;
- this.userId=userId;
- if(webSocketMap.containsKey(userId)){
- webSocketMap.remove(userId);
- //加入set中
- webSocketMap.put(userId,this);
- }else{
- //加入set中
- webSocketMap.put(userId,this);
- //在线数加1
- addOnlineCount();
- }
- log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
- sendMessage("连接成功");
- }
-
- /**
- * 连接关闭
- * 调用的方法
- */
- @OnClose
- public void onClose() {
- if(webSocketMap.containsKey(userId)){
- webSocketMap.remove(userId);
- //从set中删除
- subOnlineCount();
- }
- log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
- }
-
- /**
- * 收到客户端消
- * 息后调用的方法
- * @param message
- * 客户端发送过来的消息
- **/
- @OnMessage
- public void onMessage(String message, Session session) {
- log.info("用户消息:"+userId+",报文:"+message);
- //可以群发消息
- //消息保存到数据库、redis
- if(StringUtils.isNotBlank(message)){
- try {
- //解析发送的报文
- JSONObject jsonObject = JSON.parseObject(message);
- //追加发送人(防止串改)
- jsonObject.put("fromUserId",this.userId);
- String toUserId=jsonObject.getString("toUserId");
- //传送给对应toUserId用户的websocket
- if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){
- webSocketMap.get(toUserId).sendMessage(message);
- }else{
- //否则不在这个服务器上,发送到mysql或者redis
- log.error("请求的userId:"+toUserId+"不在该服务器上");
- }
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- }
-
-
- /**
- * @param session
- * @param error
- */
- @OnError
- public void onError(Session session, Throwable error) {
-
- log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
- error.printStackTrace();
- }
-
- /**
- * 实现服务
- * 器主动推送
- */
- public void sendMessage(String message) {
- try {
- this.session.getBasicRemote().sendText(message);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- /**
- *发送自定
- *义消息
- **/
- public static void sendInfo(String message, String userId) {
- log.info("发送消息到:"+userId+",报文:"+message);
- if(StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)){
- webSocketMap.get(userId).sendMessage(message);
- }else{
- log.error("用户"+userId+",不在线!");
- }
- }
-
- /**
- * 获得此时的
- * 在线人数
- * @return
- */
- public static synchronized int getOnlineCount() {
- return onlineCount;
- }
-
- /**
- * 在线人
- * 数加1
- */
- public static synchronized void addOnlineCount() {
- WebSocketServer.onlineCount++;
- }
-
- /**
- * 在线人
- * 数减1
- */
- public static synchronized void subOnlineCount() {
- WebSocketServer.onlineCount--;
- }
-
- }
-
在自己的Controller写个方法调用WebSocketServer.sendInfo()。
程序中使用定任务不停的向客户端发送消息
- @Controller
- @RequestMapping("/api/test")
- @Api(description = "服务器向客户端推送消息接口", tags = "Test")
- public class TestController {
-
- @Autowired
- private TestServiceImpl testServiceImpl;
- /**
- * 启动页面
- * @return
- */
- @GetMapping("/start")
- public String start(){
- return "index";
- }
-
- @PostMapping("/pushToWeb")
- @ApiOperation(value = "服务器端向客户端推送消息", notes = "服务器端向客户端推送消息")
- public ResponseBean> pushToWeb(@RequestBody @ApiParam(value = "回收人编码和医院编码", required = true) CodesInfo info){
-
- testServiceImpl.printTime();
- return new ResponseBean<>(200, "success", "123456");
- }
-
- }
-
- @Service
- @EnableScheduling
- public class TestServiceImpl {
-
- //打印时间
- @Scheduled(fixedRate=1000) //1000毫秒执行一次
- public void printTime(){
-
- SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
- String date = dateFormat.format(new Date());
- WebSocketServer.sendInfo(date,"10");
- System.out.println(date);
- }
-
- }
- html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>websocket通讯title>
- head>
- <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">script>
- <script>
- let socket;
- function openSocket() {
-
- const socketUrl = "ws://localhost:9091/api/pushMessage/" + $("#userId").val();
- console.log(socketUrl);
- if(socket!=null){
- socket.close();
- socket=null;
- }
- socket = new WebSocket(socketUrl);
- //打开事件
- socket.onopen = function() {
- console.log("websocket已打开");
- };
- //获得消息事件
- socket.onmessage = function(msg) {
- console.log(msg.data);
- //发现消息进入,开始处理前端触发逻辑
- };
- //关闭事件
- socket.onclose = function() {
- console.log("websocket已关闭");
- };
- //发生了错误事件
- socket.onerror = function() {
- console.log("websocket发生了错误");
- }
- }
- function sendMessage() {
-
- socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
- console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
- }
- script>
- <body>
- <p>【socket开启者的ID信息】:<div><input id="userId" name="userId" type="text" value="10">div>
- <p>【客户端向服务器发送的内容】:<div><input id="toUserId" name="toUserId" type="text" value="20">
- <input id="contentText" name="contentText" type="text" value="hello websocket">div>
- <p>【操作】:<div><a onclick="openSocket()">开启socketa>div>
- <p>【操作】:<div><a onclick="sendMessage()">发送消息a>div>
- body>
-
- html>

