跳至主要內容

SpringBoot集成WebSocket

sixkey大约 7 分钟后端SpringBootWebSocket实战

SpringBoot集成WebSocket

聊天室展示

导入依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sixkey</groupId>
  <artifactId>websocket</artifactId>
  <version>1.0-SNAPSHOT</version>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.12</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--websocket依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>2.0.21</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- maven打包插件 -> 将整个工程打成一个 fatjar -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!-- 作用:项目打成jar,同时把本地jar包也引入进去 -->
        <configuration>
          <includeSystemScope>true</includeSystemScope>
        </configuration>
      </plugin>
      <!--添加配置跳过测试-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.1</version>
        <configuration>
          <skipTests>true</skipTests>
        </configuration>
      </plugin>
    </plugins>

    <!-- 固定Jar包名字 -->
    <finalName>websocket</finalName>
  </build>
</project>

1、配置类

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

2、websocket调用接口方法

//注册成组件
@Component
//定义websocket服务器端,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址
//前端访问路径:ws://localhost:8089/websocket/tom
@ServerEndpoint("/websocket/{username}")
@Slf4j
public class WebSocket {

    //实例一个session,这个session是websocket的session
    private Session session;

    //存放当前用户名
    private String userName;

    //存放需要接受消息的用户名
    private String toUserName;

    //存放在线的用户数量
    private static Integer userNumber = 0;

    //存放websocket的集合,保证用户不重复
    private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();

    //前端请求一个websocket时
    //接收前端上线请求
    @OnOpen
    public void onOpen(Session session,@PathParam("username") String username) throws IOException {
        this.session = session;
        //将当前对象放入webSocketSet
        webSocketSet.add(this);
        //增加在线人数
        userNumber++;
        //保存当前用户名
        this.userName = username;
        //获得所有的用户
        Set<String> userLists = new TreeSet<>();
        for (WebSocket webSocket : webSocketSet) {
            userLists.add(webSocket.userName);
        }

        //将所有信息包装好传到客户端(给所有用户)
        Map<String, Object> map1 = new HashMap();
        //  把所有用户列表
        map1.put("onlineUsers", userLists);
        //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
        map1.put("messageType", 1);
        //  返回用户名
        map1.put("username", username);
        //  返回在线人数
        map1.put("number", this.userNumber);
        //发送给所有用户谁上线了,并让他们更新自己的用户菜单
        sendMessageAll(JSON.toJSONString(map1),this.userName);
        log.info("【websocket消息】有新的连接, 总数:{}", this.userNumber);

        // 更新在线人数(给所有人)
        Map<String, Object> map2 = new HashMap();
        //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
        map2.put("messageType", 3);
        //把所有用户放入map2
        map2.put("onlineUsers", userLists);
        //返回在线人数
        map2.put("number", this.userNumber);
        // 消息发送指定人(所有的在线用户信息)
        sendMessageAll(JSON.toJSONString(map2),this.userName);
    }

    //前端关闭时一个websocket时
    @OnClose
    public void onClose() throws IOException {
        //从集合中移除当前对象
        webSocketSet.remove(this);
        //在线用户数减少
        userNumber--;

        //通知下线
        Map<String, Object> map1 = new HashMap();
        //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
        map1.put("messageType", 2);
        //所有在线用户
        map1.put("onlineUsers", this.webSocketSet);
        //下线用户的用户名
        map1.put("username", this.userName);
        //返回在线人数
        map1.put("number", userNumber);
        //发送信息,所有人,通知谁下线了
        sendMessageAll(JSON.toJSONString(map1),this.userName);

        //通知修改用户列表
        // 更新在线人数(给所有人)
        Map<String, Object> map2 = new HashMap();
        //获得所有的用户
        Set<String> userLists = new TreeSet<>();
        for (WebSocket webSocket : webSocketSet) {
            userLists.add(webSocket.userName);
        }
        //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
        map2.put("messageType", 3);
        //把所有用户放入map2
        map2.put("onlineUsers", userLists);
        //返回在线人数
        map2.put("number", this.userNumber);
        // 消息发送指定人(所有的在线用户信息)
        sendMessageAll(JSON.toJSONString(map2),this.userName);
        log.info("【websocket消息】连接断开, 总数:{}", webSocketSet.size());
    }

    //前端向后端发送消息
    //后端接收前端发送过来的消息
    @OnMessage
    public void onMessage(String message) throws IOException {
        log.info("【websocket消息】收到客户端发来的消息:{}", message);
        //将前端传来的数据进行转型
        com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(message);
        //获取所有数据
        String textMessage = jsonObject.getString("message");
        String username = jsonObject.getString("username");
        String type = jsonObject.getString("type");
        String tousername = jsonObject.getString("tousername");
        //群发
        if(type.equals("群发")){
            Map<String, Object> map3 = new HashMap();
            map3.put("messageType", 4);
            //所有在线用户
            map3.put("onlineUsers", this.webSocketSet);
            //发送消息的用户名
            map3.put("username", username);
            //返回在线人数
            map3.put("number", userNumber);
            //发送的消息
            map3.put("textMessage", textMessage);
            //发送信息,所有人,通知谁下线了
            sendMessageAll(JSON.toJSONString(map3),this.userName);
        }
        //私发
        else{
            //发送给对应的私聊用户
            Map<String, Object> map3 = new HashMap();
            map3.put("messageType", 4);
            //所有在线用户
            map3.put("onlineUsers", this.webSocketSet);
            //发送消息的用户名
            map3.put("username", username);
            //返回在线人数
            map3.put("number", userNumber);
            //发送的消息
            map3.put("textMessage", textMessage);
            //发送信息,所有人,通知谁下线了
            sendMessageTo(JSON.toJSONString(map3),tousername);

            //发送给自己
            Map<String, Object> map4 = new HashMap();
            map4.put("messageType", 4);
            //所有在线用户
            map4.put("onlineUsers", this.webSocketSet);
            //发送消息的用户名
            map4.put("username", username);
            //返回在线人数
            map4.put("number", userNumber);
            //发送的消息
            map4.put("textMessage", textMessage);
            //发送信息,所有人,通知谁下线了
            sendMessageTo(JSON.toJSONString(map3),username);
        }
    }

    /**
     *  消息发送所有人
     */
    public void sendMessageAll(String message, String FromUserName) throws IOException {
        for (WebSocket webSocket: webSocketSet) {
            //消息发送所有人(同步)getAsyncRemote
            webSocket.session.getBasicRemote().sendText(message);
        }
    }

    /**
     *  消息发送指定人
     */
    public void sendMessageTo(String message, String ToUserName) throws IOException {
        //遍历所有用户
        for (WebSocket webSocket : webSocketSet) {
            if (webSocket.userName.equals(ToUserName)) {
                //消息发送指定人
                webSocket.session.getBasicRemote().sendText(message);
                log.info("【发送消息】:", this.userName+"向"+ToUserName+"发送消息:"+message);
                break;
            }
        }
    }
}

3、启动类

@SpringBootApplication
public class WebSocketApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebSocketApplication.class,args);
    }
}

4、配置文件

server:
  port: 8090

5、前端页面

<!DOCTYPE HTML>
<html>
<head>
    <title>聊天室</title>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
	<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
	<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
 
<body style="padding-left: 200px;padding-right: 200px;margin-top: 40px;">
	<div class="alert alert-primary" style="margin-bottom: -20px;text-align: center;" role="alert">聊天室</div>
    <div class="jumbotron" style="height: 500px;width: 1135px;">
		<!-- 用户列表 -->
		<div class="jumbotron" style="height: 500px;width: 200px;float: right;background-color: green;margin-top: -65px;margin-right: -30px;">
			<div class="alert alert-primary" role="alert" style="width: 200px;margin-top: -43px;margin-left: -32px;">在线用户列表:</div>
			<ul class="list-group" id="userlist" style="width: 200px;margin-top: -18px;margin-left: -32px;">
 
			</ul>
		</div>
		<div id="content" style="height: 500px;width: 900px;background-color: white;margin-top: -45px;margin-left: -20px;">
			
		</div>
    </div>
	<div class="form-group">
	    <input type="text" style="width: 90%;;float: left;" class="form-control" id="message"  placeholder="请输入内容">
		<button type="button" style="width: 10%;float: left;" class="fasong">发送</button>
		<button type="button" style="width: 10%;float: left;" class="qingping">清屏</button>
	</div>
</body>
 
<script type="text/javascript">
	
	//私发全局消息
	var tomessage = "";
	//私发用户名称
	var tousername = "";
	//清屏
	$(".qingping").click(function(){
		$("#content").html(``);
	})
	
	//生成一个随机用户名
	var username = "用户"+Math.random()*100;
	
	//定义一个websocket
    var websocket = null;
 
    //判断当前浏览器是否支持WebSocket(固定写法)
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8090/websocket/"+username);
    }else{
        alert('浏览器不支持websocket')
    }
 
    //连接发生错误的回调方法
    websocket.onerror = function(){
        console.log("发生错误");
    };
 
    //连接成功建立的回调方法
    websocket.onopen = function(event){
        console.log("建立连接"+"event");
    }
 
    //接收到消息的回调方法
    websocket.onmessage = function(event){
		var data = JSON.parse(event.data);
		console.log(JSON.parse(event.data))
		
		//更新content的内容(上线)
		if(data.messageType=="1"){
			// console.log("成功")
			$("#content").append(`<span style="width: 100%;height: 30px;line-height: 30px;font-size: 14px;">`+data.username+"上线了"+`</span><br>`);
		}
		//更新content的内容(下线)
		if(data.messageType=="2"){
			// console.log("成功")
			$("#content").append(`<span style="width: 100%;height: 30px;line-height: 30px;font-size: 14px;">`+data.username+"下线了"+`</span><br>`);
		}
		//更新content的内容(更新用户列表)
		if(data.messageType=="3"){
			//先清空
			$("#userlist").html(``);
			var list = data.onlineUsers;
			console.log(list)
			for(var i=0;i<list.length;i++){
				$("#userlist").append(`<li style="cursor:pointer;" class="list-group-item" onclick="friend(this)" values="`+list[i]+`">`+list[i]+`</li>`);
			}
		}
		//更新content的内容(更新用户群发消息)
		if(data.messageType=="4"){
			// console.log(data);
			$("#content").append(`<span style="width: 100%;height: 30px;line-height: 30px;font-size: 14px;">`+data.username+`说:`+data.textMessage+`</span><br>`);
		}
		
	}
 
	//选择用户
	function friend(e){
		console.log(e);
		$("#message").val("@ "+e.innerHTML+" ");
		var data = e.innerHTML;
		console.log(data);
		tousername = data;
	}
 
    //连接关闭的回调方法
    websocket.onclose = function(){
        console.log("关闭连接");
    }
 
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
		alert("已关闭连接");
        websocket.close();
    }
 
	//发送按钮
	$(".fasong").click(function(){
		
		console.log("发送消息")
		var message = $("#message").val();
		//判断是群发还是私发
		console.log(message);
		//获取发送对象
		tousername = message.split(' ')[1];
		//获取发送消息
		tomessage = message.split(' ')[2];
		console.log(tomessage);
		console.log(tousername);
		if(message.indexOf("@")!=-1){
			//私发
			console.log("私发")
			var param = {};
			param['username'] = username;
			param['message'] = tomessage;
			param['type'] = '私发';
			param['tousername'] = tousername;
		}
		else{
			//群发
			console.log("群发")
			var param = {};
			param['username'] = username;
			param['message'] = message;
			param['type'] = '群发';
			param['tousername'] = '';
		}
		//发送消息到后端
		websocket.send(JSON.stringify(param));
	})
	
</script>
</html>