Spring Boot 集成 WebSocket 并实现聊天室功能实例

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

 

为什么要使用 WebSocket
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
 
 
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
 
创建WebSocket对象
我们可以用一下方式创建 WebSocket 对象。
var Socket = new WebSocket(url, [protocol] );
 

websocket事件与方法

以下是 WebSocket 对象的相关事件。
事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

以下是 WebSocket 对象的相关方法。
方法 描述
Socket.send()

使用连接发送数据

Socket.close()

关闭连接

 

Spring Boot 集成 WebSocket

引入依赖

SpringBoot2.0对WebSocket的支持很简单,直接引入starter依赖就可以了。

gradle方式

// websocketcompile('org.springframework.boot:spring-boot-starter-websocket')
maven方式
<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-websocket</artifactId>  </dependency> 
 
配置WebSocket
配置也很简单,我们在这里只做一个很简单配置,即只实例化一个 ServerEndpointExporter 对象。
package fun.imcoder.cloud.config;
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/** * 开启WebSocket支持 * @author cdd */@Configurationpublic class WebSocketConfig {
    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }
}
创建websocket端点
这里我们使用 @ServerEndpoint 创建一个注解式的端点,我们只需要在该端点内
实现WebSocket的事件即可,这里我们使用用 @OnOpen @OnClose @OnMessage @OnError 注解来处理WebSocket事件。
package fun.imcoder.cloud.websocket;
import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;
import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/websocket/{name}")@Component@Slf4jpublic class WebSocketServer {
    /**     * 连接建立成功调用的方法     */    @OnOpen    public void onOpen(Session session, @PathParam("name") String name)         log.info("WebSocket 打开连接");    }
    /**     * 连接关闭调用的方法     */    @OnClose    public void onClose() {        log.info("WebSocket 断开连接");    }
    /**     * 收到客户端消息后调用的方法     *     * @param msg 客户端发送过来的消息     */    @OnMessage    public void onMessage(String msg, Session session) {        log.info("收到消息:" + msg);    }
    /**     * 出错时调用的方法     * @param session     * @param e     */    @OnError    public void onError(Session session, Throwable e) {        log.error("WebSocket 出错");        e.printStackTrace();    }}
到这里我们就成功使用Spring Boot集成了WebSocket,下面我们简单的搭建一个聊天室来更好的理解WebSocket。
搭建简易聊天室
我们在刚才的基础上修改下WebSocket的端点,对相关事件进行处理。
 
配置websocket端点
package fun.imcoder.cloud.websocket;
import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;
import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/websocket/{name}")@Component@Slf4jpublic class WebSocketServer {
    // 当前在线连接数    private static int onlineCount = 0;    // 存放每个客户端对应的WebSocket对象    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据    private Session session;
    // 接收 name    private String name = "";
    /**     * 连接建立成功调用的方法     */    @OnOpen    public void onOpen(Session session, @PathParam("name") String name) {        this.session = session;        webSocketSet.add(this);        addOnlineCount();           // 在线数加1        log.info(name + " 加入聊天室,当前在线人数为" + onlineCount);        this.name = name;        try {            sendMessage("{"name":"" + name + "","msg":"进入聊天室","onlineCount":""+onlineCount+""}", null);        } catch (IOException e) {            log.error("websocket IO异常");        }    }
    /**     * 连接关闭调用的方法     */    @OnClose    public void onClose() {        webSocketSet.remove(this);        subOnlineCount();           // 在线数减1        log.info(name + "退出聊天室!当前在线人数为" + onlineCount);    }
    /**     * 收到客户端消息后调用的方法     *     * @param msg 客户端发送过来的消息     */    @OnMessage    public void onMessage(String msg, Session session) {        log.info("收到来自" + name + "的消息:" + msg);        // 群发消息        for (WebSocketServer item : webSocketSet) {            try {                item.sendMessage("{"name":"" + name + "","msg":"" + msg + "","onlineCount":""+onlineCount+""}");            } catch (IOException e) {                e.printStackTrace();            }        }    }
    /**     * 出错时调用的方法     * @param session     * @param e     */    @OnError    public void onError(Session session, Throwable e) {        log.error("WebSocket 出错");        e.printStackTrace();    }
    /**     * 实现服务器主动推送     */    public void sendMessage(String message) throws IOException {        this.session.getBasicRemote().sendText(message);    }

    /**     * 群发自定义消息     */    public static void sendMessage(String message, String name) throws IOException {        for (WebSocketServer item : webSocketSet) {            try {                //这里可以设定只推送给这个name的,为null则全部推送                if (name == null) {                    item.sendMessage(message);                } else if (item.name.equals(name)) {                    log.info("推送消息给" + name + ",推送内容:" + message);                    item.sendMessage(message);                    break;                }            } catch (IOException e) {                continue;            }        }    }
    public static synchronized int getOnlineCount() {        return onlineCount;    }
    public static synchronized void addOnlineCount() {        WebSocketServer.onlineCount++;    }
    public static synchronized void subOnlineCount() {        WebSocketServer.onlineCount--;    }}
创建页面

代码如下

<!DOCTYPE html><html>
<head>  <meta charset='UTF-8'>  <title>websocket</title>  <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>  <style>    div {      padding: 7px;    }
    #chat {      border: 1px solid #999;      margin-top: 20px;      width: 500px;      height: 400px;    }</style></head>
<body>  <div>    <div>当前在线人数:<span id="onlineCount"></span></div>    <div>      姓名:<input id="name"><button onclick="connection()">连接</button>      <input id="msg" style="margin-left: 20px;"><button onclick="sendMsg()">发送</button>    </div>    <div id="chat">
    </div>  </div>  <script>
    let name;    let socket;    if (typeof (WebSocket) == 'undefined') {      console.log('您的浏览器不支持 WebSocket');    } else {      console.log('您的浏览器支持 WebSocket');
    }    function connection() {      // 实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接        name = $('#name').val();      if (!name) {        alert('请输入姓名')        return      }      socket = new WebSocket('ws://localhost:8080/websocket/' + name);      // 打开事件        socket.onopen = function () {        console.log('WebSocket 连接成功');      };      // 收到消息事件        socket.onmessage = function (datas) {        // 收到消息开始处理前端触发逻辑        let data = JSON.parse(datas.data)        $('#chat').append('<div>' + data.name + ':' + data.msg + '</div>')        $('#onlineCount').html(data.onlineCount)      };      //关闭事件        socket.onclose = function () {        console.log('WebSocket 断开连接');        $('#chat').append('<div style="color:red;">服务器连接中断</div>')      };      //发生了错误事件        socket.onerror = function () {        alert('WebSocket 发生了错误');      }    }    function sendMsg() {      let msg = $('#msg').val();      socket.send(msg);    }</script></body>
</html>
 

打卡页面控制台,可以看到有如下输出

下面我们测试一下,输入姓名后,点击连接。

控制台输出

这时,我们再重新打开一个页面,重复上面操作

之前的页面会同步显示张三进入聊天室

我们再来发消息试一下

张三页面显示如下

同样,张三发消息后,imcoder也一样能收到。

后台控制台打印如下

我们再测试一下从后台群发消息
编写一个controller,注入 WebSocketServer 如下
package fun.imcoder.cloud.controller;
import com.alibaba.fastjson.JSON;import fun.imcoder.cloud.websocket.WebSocketServer;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;import java.io.IOException;import java.util.Map;
@RestController@RequestMapping("/user")public class UserController{
    @Resource    private WebSocketServer webSocketServer;
    @RequestMapping("/testWebsocket")    private void testWebsocket(@RequestBody Map<String, Object> map) throws IOException {        webSocketServer.sendMessage(JSON.toJSONString(map), null);    }}

然后我们使用postman调用该接口

两个页面都会显示后台发送的消息

关于WebSocket的简单介绍就到这里了,更多详细的用法后续在介绍,如有错误还请提出指正。

未经允许不得转载:大自然的搬运工 » Spring Boot 集成 WebSocket 并实现聊天室功能实例

赞 (0)

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址