天天看点

springBoot整合WebSocket实现的聊天室demo,可向指定客户端发送消息

直接上代码

首先加入依赖

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

然后将此类注入到spring容器中:

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();
    }

}
           

建立连接,关闭连接,发送消息 主要代码如下:

/**
 * Copyright [2016-2017] [yadong.zhang]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.yudi.websocket;

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.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 */
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class MyWebSocket {
    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
   // private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    /**3
     * 为了区分不同的客户端改用ConcurrentHashMap,每次客户端建立一个连接,就会存入一条记录,key为客户端的userId,value为socket连接实例
     * 这样可以通过 userId来调用其指定的连接进行操作。
     */
    private static ConcurrentHashMap<String,MyWebSocket> webSocketMap = new ConcurrentHashMap<String,MyWebSocket>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) {
        webSocketMap.forEach((k,v)->{
            try {
                v.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }

        });
    }
    /**
     * 向指定userId发送消息
     */
    public static void sendInfo(String userId,String message) {
        webSocketMap.forEach((k,v)->{
            try {
                if (k.equals(userId)){
                    v.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        });
    }
    /**
     * 连接建立成功调用的方法
     *
     */
    @OnOpen
    public void onOpen(@PathParam("userId")String userId, Session session) {
        System.out.println(userId);
        this.session = session;
        webSocketMap.put(userId,this);
        List list = new ArrayList();
        webSocketMap.forEach((k,v)->list.add(k));
//        addOnlineCount();
        System.out.println("有新连接加入!当前在线人数为" + webSocketMap.size());
        sendInfo("当前共有" + webSocketMap.size() + " 位用户在线,userId是:" +list.toString());

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("userId")String userId) {
        webSocketMap.remove(userId);
        sendInfo("当前共有" + webSocketMap.size() + " 位用户在线");
        System.out.println("有一连接关闭!当前在线人数为" + webSocketMap.size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     *         客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);

        //群发消息
        sendInfo(message);
    }
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
}
           

配置文件添加freemarker相关配置:

freemarker:
      allow-request-override: false
      allow-session-override: false
      cache: false
      charset: UTF-8
      check-template-location: true
      content-type : text/html
      enabled : true
      expose-request-attributes : false
      expose-session-attributes: false
      expose-spring-macro-helpers: true
      prefer-file-system-access: true
      suffix: .ftl
      template-loader-path: classpath:/templates/
      order: 1
      settings:
        template_update_delay: 0
        default_encoding: UTF-8
        classic_compatible: true
           

客户端代码:

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSocket测试 | 聊天小程序</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" target="_blank" rel="external nofollow" >
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/4.0.0-beta/js/bootstrap.min.js"></script>
</head>

<body>
<div class="container">
    <div class="row">
        <div class="col">
            <form>
                <div class="form-group">
                    <label for="comment">聊天框:</label> <textarea class="form-control" rows="5" id="text">你好啊</textarea>
                </div>
                <div class="form-group">
                    <button onclick="send()" type="button" class="btn btn-primary">发送</button>
                    <button onclick="closeWebSocket()" type="button" class="btn btn-danger">关闭</button>
                </div>
                <div>
                    <ul class="list-group" id="message"></ul>
                </div>
            </form>
        </div>
    </div>
</div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:8100/websocket/${userId}");
        //连接发生错误的回调方法
        websocket.onerror = function () {
            setMessageInnerHTML("与服务器连接失败...");
        };

        //连接成功建立的回调方法
        websocket.onopen = function (event) {
            setMessageInnerHTML("与服务器连接成功...");
        }

        //接收到消息的回调方法
        websocket.onmessage = function (event) {
            setMessageInnerHTML(event.data);
        }

        //连接关闭的回调方法
        websocket.onclose = function () {
            setMessageInnerHTML("已关闭当前链接");
        }

        //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function () {
            websocket.close();
        }
    } else {
        alert('Not support websocket');
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        $("#message").append("<li class=\"list-group-item\">" + innerHTML + "</li>");
    }

    //关闭连接
    function closeWebSocket() {
        websocket.close();
    }

    //发送消息
    function send() {
        websocket.send($("#text").val());
    }
</script>
</html>
           

测试用controller:

package com.yudi.websocket.controller;
import com.yudi.websocket.MyWebSocket;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/websocket")
public class WebSocketController {
    /**
     * 给所有人发消息/指定userId发送消息
     * @param message
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/sendMessage", method = RequestMethod.GET)
    public ResponseEntity<?> sendMessage(String userId,String message) {
        if (StringUtils.isEmpty(userId)){
            //向全部人发送消息
            MyWebSocket.sendInfo(message);
        }else {
            //向指定userId发送消息
            MyWebSocket.sendInfo(userId,message);
        }


        return new ResponseEntity(null,HttpStatus.OK);
    }


    /**
     * 登入聊天室
     * @param userId 用户id,每个人唯一,每一个userId会建立一个websocket连接
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/index")
    public String index(String userId, HttpServletRequest request, HttpServletResponse response) {
        request.setAttribute("userId",userId);
        return "index";
    }
}
           

效果图:

springBoot整合WebSocket实现的聊天室demo,可向指定客户端发送消息

向指定userId发送消息:http://localhost:8100/websocket/sendMessage?message=xxxx&userId=2 

继续阅读