WebSocket的简单介绍和在SpringBoot中的实战

WebSocket是什么

嗯,简单一点来理解,WS其实也是一种网络通信协议。

从HTML5 开始,提供了一种基于单个TCP连接上进行全双工通讯的协议,这个协议就是WebSocket协议。

在没WebSocket之前,都只能由客户端发起请求,服务端接收并相应请求,这样很多功能的实现就只能通过轮训服务端接口来解决,带来了很多不便利性。

这就好像我有一个快递,为了快递有没有到这件事,我必须要每天都主动去营业点问一下,这样的单向查询方式很低效且浪费时间,那有没有什么办法可以让营业点在快递到的时候主动通知我呢?

有,那就是预留我们的手机号码,快递到了的时候营业点给我们发个短信就行。

那对应到wb,营业点就是服务端,我就是客户端,手机号码就是session。

由此,引出在wb中有三个重要的概念,服务器客户端和session。

服务端持有与某个客户端连接的session,只要服务器有结果,就能通过这个session发送到指定的客户端中。

Springboot中的实战

好,那让我们在springboot中来实现一下wb的功能,得益于tomact对wb的支持,在sb中能够很轻松的构造出一个wb的应用。

引包

在springboot中,想要使用wb,只要引入下面这个包,就能够支持wb

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

服务端

首先,构造一个Service类,需要注意的是,这个类上面的注解要用@ServerEndpoint,以表明这个一个wb的服务接入点。

package com.tmx.mengxiangspringcloud.websocket;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/websocket1")
public class WebsocketService {
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    //key 可以为用户名、支付订单号、用户id等
    public static ConcurrentHashMap<String, WebsocketService> webSocketMap = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        //加入map
        try {
            webSocketMap.put("1",this);
            sendMessage(String.valueOf("加入成功啦"));
        } catch (IOException e) {
        }
    }
    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove("1");
    }
    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        WebsocketService websocketService = webSocketMap.get("1");
        websocketService.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }
    /**
     * 列表群发
     */
    public static void sendInfo(String message) throws IOException {
        for (String key : webSocketMap.keySet()) {
            try {
                webSocketMap.get(key).sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }
}

变量讲解

可以看到,在成员变量中,我们构建了一个ConcurrentHashMap变量,这个变量就是用来保存服务器和客户端session的关系。

方法讲解

在方法中,我们一共使用了两个注解,一个注解是@onOpen,这个注解的意思是在客户端与服务器建立连接时需要执行的逻辑;一个注解是@onClose,这个注解的意思是在客户端与服务器关闭连接时需要执行的逻辑。
sendMessage方法是向客户端发送消息的;sendInfo是向所有客户端群发消息的。

消息发送

详见sendMessage方法,服务器向客户端发送消息,是通过获取某个客户端的session,再利用session的sendText方法发送消息。

客户端

客户端需要通过ws协议连接到服务端,以下通过html页面的方式实现

DOCTYPE html>

<html>

<head>

    <title>Simple WebSocket Exampletitle>

head>

<body>

<script>

    var socket = new WebSocket("ws://127.0.0.1:8082/websocket1");

    socket.onopen = function() {

        console.log("Connection established.");

        socket.send("Hello, server!");

    };

    socket.onmessage = function(event) {

        console.log("Received: " + event.data);

    };

    socket.onclose = function(event) {

        console.log("Connection closed.");

    };

script>

body>

html>

试试效果

首先,启动服务端,然后浏览器中打开index.html页面,按F12进入控制台。

WebSocket的简单介绍和在SpringBoot中的实战_第1张图片
可以看到,客户端向服务器发起请求已经成功并创建了一个session对象,并且服务器发送的消息客户端也已经接受到了,但是服务器如何主动向客户端推送消息呢?请接着往下看。

服务器通过调用接口向客户端发送消息

我们定义一个Controller向客户端主动发送消息,其中调用的是WebsocketService中的sendMessage方法。

package com.tmx.mengxiangspringcloud.controller;

import com.tmx.mengxiangspringcloud.websocket.WebsocketService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class HelloController1 {
    @Resource
    private WebsocketService websocketService;
    @RequestMapping("/sendMsg")
    public String sendMsg(){
        try {
            websocketService.sendMessage("哈哈哈哈为所欲为");
           // webSocketSession.sendMessage(new org.springframework.web.socket.TextMessage("Hello, client!"));
        }catch (Exception e){
            e.printStackTrace();
        }
        return "hello world";
    }
}

发送消息

打开浏览器,输入http://127.0.0.1:8082/sendMsg调用接口,可以看到客户端控制台中已经输出了服务器发送的内容

WebSocket的简单介绍和在SpringBoot中的实战_第2张图片

优化

至此,我们就实现了一个简单wb应用,但是还存在很多可以优化的点,比如在往ConcurrentHashMap中设值的时候写死了一个1作为key,这样就会导致所有的客户端连接session都会被覆盖,优化的方向可以是一个客户端请求的时候带上唯一请求值,比如说订单号、交易单号、ip地址等等。同时,在sendMessage方法中,通过key取到对应的session发送消息。

结语

wb的出现,是为了解决服务器不能往客户端发消息的痛点,这样做的好处是提高了效率,但是也需要考虑服务端同时维护的session数,如果session数过多,但资源不足,会引起很多生产事故,还需要考虑与前端的合作,因为这个协议是需要前后端都支持的,所以能不能使用wb,还需要看实际。

你可能感兴趣的:(websocket,spring,boot,网络协议)