springboot整合websocket群聊

org.springframework.boot spring-boot-starter-websocket

application.yml

server:
  port: 1018
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true
spring:
  application:
    name: testexe
  mvc:
    view:
      prefix: /
      suffix: .html
package com.testexe.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket配置类
 */
@Configuration
public class WebSocketConfig {
    //实例化一个Bean对象
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

package com.testexe.commont;
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.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 工具类
 */
@Slf4j
@Component
@ServerEndpoint("/groupChat/{Group_no}/{username}")
public class GroupChatCom {

    // 保存 组id->组成员 的映射关系 之所以使用ConcurrentHashMap因为这个是线程安全的,里面采用了分段锁而HashMap是线程不安全的
    private static ConcurrentHashMap<String, List<Session>> groupMemberInfoMap = new ConcurrentHashMap<>();

    // 收到消息调用的方法,群成员发送消息
    @OnMessage
    public void onMessage(@PathParam("Group_no") String Group_no,
                          @PathParam("username") String username, String message) {
        //得到当前群的所有会话,也就是所有用户
        List<Session> sessionList = groupMemberInfoMap.get(Group_no);
        // 遍历Session集合给每个会话发送文本消息
        sessionList.forEach(item -> {
            try {
                String text = username + ": " + message;
                item.getBasicRemote().sendText(text);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 建立连接调用的方法,群成员加入
     * @param session 会话
     * @param Group_no 群id
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("Group_no") String Group_no) {
        //得到当前群的所有会话,也就是所有用户
        List<Session> sessionList = groupMemberInfoMap.get(Group_no);
        if (sessionList == null) {
            sessionList = new ArrayList<>();
            groupMemberInfoMap.put(Group_no,sessionList);
        }
        sessionList.add(session);
        log.info("连接建立");
        log.info("群号: {}, 群人数: {}", Group_no, sessionList.size());
    }

    // 关闭连接调用的方法,群成员退出
    @OnClose
    public void onClose(Session session, @PathParam("Group_no") String Group_no) {
        List<Session> sessionList = groupMemberInfoMap.get(Group_no);
        sessionList.remove(session);
        log.info("连接关闭");
        log.info("群号: {}, 群人数: {}", Group_no, sessionList.size());
    }

    // 传输消息错误调用的方法
    @OnError
    public void OnError(Throwable error) {
        log.info("连接出错:{}",error.getMessage());
    }
}

package com.testexe.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

    @RequestMapping("/index")
    public String index() {
        return "index";
    }
}

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>测试WebSocket</title>
</head>
<body>
<div>

</div>
    <div style="align-content: center">
        <h1>WebSocket群聊</h1>
        <input type="text" id="Group_no" placeholder="请输入房间号"/><br>
        <input type="text" id="nickname" placeholder="请输入昵称"/><br>
        <input type="submit" value="连接" onclick="connect()" /><br>
        <textarea rows="3" cols="20" id="content"></textarea><br>
        <input type="submit" value="发送" onclick="start()" />
        <br>
    </div>
    <div id="messages"></div>
<script type="text/javascript">
    var webSocket = null;
    //收到消息
    function onMessage(event) {
        document.getElementById('messages').innerHTML
            += '<br />' + event.data;
    }
    //建立连接
    function onOpen(event) {
        document.getElementById('messages').innerHTML
            = '连接已经建立';
    }
    //发生错误
    function onError(event) {
        alert("发生错误");
        webSocket = null;
    }
    //连接关闭
    function onClose(event) {
        alert("连接关闭");
        webSocket = null;
    }
    //连接
    function connect() {
        //获取群号
        var Group_no = document.getElementById('Group_no').value;
        //获取昵称
        var nickname = document.getElementById('nickname').value;
        //验证非法数据
        if (url == '' || nickname == '') {
            alert("群号和用户名不能为空");
            return;
        }
        //验证是否已经建立连接
        if(webSocket!=null){
            alert("已经建立过连接,如需重新建立连接,请自行更改逻辑,或者重新刷新页面");
            return;
        }
        //创建Websocket连接url
        var url = 'ws://localhost:1018/groupChat/' + Group_no + '/' + nickname;
        //实例化WebSocket
        webSocket = new WebSocket(url);
        //出现错误
        webSocket.onerror = function(event) {
            onError(event)
        };
        //调用创建连接
        webSocket.onopen = function(event) {
            onOpen(event)
        };
        //调用收到消息
        webSocket.onmessage = function(event) {
            onMessage(event)
        };
        //调用关闭连接
        webSocket.onclose = function(event) {
            onClose(event)
        };
    }
    //开始发送
    function start() {
        //获取发送的内容
        var text = document.getElementById('content').value;
        if(text== ''){
            alert("发送内容不允许为空");
            return;
        }
        if(webSocket==null){
            alert("请先建立连接");
            return;
        }
        //调用WebSocket发送的方法
        webSocket.send(text);
        //初始化文本域的内容为空
        document.getElementById('content').value = '';
    }
</script>
</body>
</html>
访问路径:http://localhost:1018/index

websocket客户端和服务端
  <!-- 引入WebSocket依赖 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- websocket作为客户端 -->
<dependency>
 <groupId>org.java-websocket</groupId>
 <artifactId>Java-WebSocket</artifactId>
 <version>1.3.8</version>
</dependency>
package com.testexe.controller;

import com.testexe.commont.MyWebSocketClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("ws-client")
public class WebsocketClient {

    @Resource
    private MyWebSocketClient webSocketClient;

    /**
     * http://localhost:1018/ws-client/send/测试
     * @param message
     */
    @GetMapping("send/{message}")
    public void sendRequest(@PathVariable String message){
        webSocketClient.send(message);
    }
}


package com.testexe.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket配置类
 */
@Configuration
public class WebSocketConfig {
    //实例化一个Bean对象
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

package com.testexe.commont;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

@Slf4j
public class MyWebSocketClient extends WebSocketClient {

    public MyWebSocketClient(URI serverUri) {
        super(serverUri);
    }

    @Override
    public void onOpen(ServerHandshake arg0) {
        log.info("------ WebSocketClient onOpen ------");
    }

    @Override
    public void onClose(int arg0, String arg1, boolean arg2) {
        log.info("------ WebSocket onClose ------{}",arg1);
    }

    @Override
    public void onError(Exception arg0) {
        log.error("------ WebSocket onError ------{}",arg0);
    }

    @Override
    public void onMessage(String response) {
        log.info("-------- 接收到服务端数据: " + response + "--------");
    }

}

package com.testexe.commont;
import org.java_websocket.client.WebSocketClient;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.URISyntaxException;

@Component
public class WebSocketClientConfigurer {

    private final String wsServerUrl = "ws://127.0.0.1:1018/api/ws/1";

    @Bean
    public WebSocketClient webSocketClient() {
        try {
            MyWebSocketClient webSocketClient = new MyWebSocketClient(new URI(wsServerUrl));
            webSocketClient.connect();
            return webSocketClient;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

}


package com.testexe.commont;
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.ConcurrentHashMap;

/**
 * 服务端
 **/
@Component
@ServerEndpoint("/api/ws/{sid}")
@Slf4j
public class WebSocketServer {

    private String sid;

    private static final ConcurrentHashMap<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    /**
     * 连接成功
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.sid = sid;
        SESSION_MAP.put(sid, session);
        log.info("有新连接:sid:{},sessionId:{},当前连接数:{}", sid, session.getId(), SESSION_MAP.size());
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose(Session session) {
        SESSION_MAP.remove(this.sid);
        log.info("连接关闭,sid:{},session id:{}!当前连接数:{}", this.sid, session.getId(), SESSION_MAP.size());
    }

    /**
     * 收到消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到客户端消息:{},内容:{}", sid, message);
        if("ping".equals(message)){
            try {
                session.getBasicRemote().sendText("pong");
            } catch (IOException e) {
                log.error("onMessage 推送服务端消息失败:{},内容:{}", sid, message);
            }
        }else{
            // 排除自己
            // sendMeasureDataInfoExcludeSelf(message, sid);
            // 发给所有客户端包括自己
            sendMeasureDataInfo(System.currentTimeMillis()+message);
        }
    }


    /**
     * 连接错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("{} 发生错误", session.getId(), error);
    }

    /**
     * 群发消息
     */
    public void sendMeasureDataInfo(String message) {
        for (String sid : SESSION_MAP.keySet()) {
            Session session = SESSION_MAP.get(sid);
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("推送服务端消息失败:{},内容:{}", sid, message);
            }
            log.info("推送服务端消息:{},内容:{}", sid, message);
        }
    }

    /**
     * 群发消息,排除消息发起者
     * @param message
     * @param sidSelf
     */
    private void sendMeasureDataInfoExcludeSelf(String message, String sidSelf){
        for(String sid : SESSION_MAP.keySet()){
            if(sidSelf.equals(sid)){
                continue;
            }
            Session session = SESSION_MAP.get(sid);
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("sendMeasureDataInfoExcludeSelf 推送服务端消息失败:{},内容:{}", sid, message);
            }
            log.info("sendMeasureDataInfoExcludeSelf 推送服务端消息:{},内容:{}", sid, message);
        }
    }
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值