前些天做了一个网站:modubox.cn 其中有个群聊插件,许多人问如何实现的。这里简单说下,为了快速完成群聊功能,我选择从最简单的 WebSocket 开始。
什么是WebSocket ?
既然要使用它,就需要了解一下它吧。WebSocket其实也是一种基于TCP的网络协议,它与HTTP协议最大的不同是:是一种双向通信协议,在建立连接后,WebSocket服务器端和客户端都能主动向对方发送或接收数据,而HTTP协议只能客户端主动发起通信。
所以WebSocket能够用于聊天,当然其他地方也能应用,如果做客服系统或推送消息都可以从这里开始。
如何实现单聊/群聊?
群聊:所有客户端的消息发送到服务器,服务端将消息发送给所有客户端。
单聊:WebSocket客户端之间是无法直接通信的,想要通信,必须由服务端转发。
群聊单聊
实现
1. 引入WebSocket的支持
我们使用当前最流行的Spring Boot框架构建项目,然后引入Spring Boot 对 WebSocket 的支持:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
复制代码
2. 开启WebSocket
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
复制代码
3. 服务端
这里主要有以下几点:
-
声明服务端点路径
-
存储所有连接用户,等待匹配用户
-
连接 onOpen,消息OnMessage,关闭onClose,错误onError 方法
-
发送消息给特定连接者
@ServerEndpoint(value = “/websocket/random/”)
@Component
public class ChatRandomServer {
//所有连接
public static ConcurrentHashMap<String, ChatRandomServer> webSocketSet = new ConcurrentHashMap<>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//所有在配对的ID
private static List webSocketLiveList = new CopyOnWriteArrayList();
//自己的id标识
private String id = “”;
//连接对象的id标识
private String toUser = “”;/** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session) { session.setMaxIdleTimeout(3600000); this.session = session; //获取用户ip String ip = IpUtil.getRemoteAddress(session); this.id = ip; ChatRandomServer put = webSocketSet.put(this.id, this); //如果已经在队里,就不去找对象 if (put == null) { try { if (pair()) { sendMessage("匹配成功"); } } catch (IOException e) { e.printStackTrace(); } } else { try { sendMessage("匹配失败"); webSocketSet.remove(this.id); session.close(); } catch (IOException e) { e.printStackTrace(); } } log.info("用户{}加入!当前在线人数为: {}", this.id, webSocketSet.size()); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { ChatRandomServer UserId = webSocketSet.get(toUser); webSocketLiveList.remove(this.id); if (UserId != null) { try { sendToUser(session, "对方已离开", toUser); } catch (IOException e) { e.printStackTrace(); } } webSocketSet.remove(this.id); log.info("{}连接关闭!当前在线人数:{}, 当前在匹配的人数:{}" ,this.id,webSocketSet.size(), webSocketLiveList.size()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, Session session) { log.info("来自 {} 的消息: {}", this.id, message); try { ChatRandomServer.sendToUser(session, message, toUser, 2); } catch (IOException e) { e.printStackTrace(); } } @OnError public void onError(Session session, Throwable error) { log.error("发生错误"); error.printStackTrace(); try { SendSelf(session,"服务器出现错误"); } catch (IOException e) { e.printStackTrace(); } } /** * 发送消息给自己 */ public void sendMessage(String message) throws IOException { SendSelf(this.session, message); } private static void SendSelf(Session session, String message) throws IOException { session.getBasicRemote().sendText(message); } /** * 发送信息给指定ID用户 */ public static void sendToUser(Session session, String message, String sendUserId) throws IOException { ChatRandomServer UserId = webSocketSet.get(sendUserId); if (UserId != null) { UserId.sendMessage(message); } else { SendSelf(session, "发送失败"); } } /** * 通知除了自己之外的所有人 */ private void sendOnlineCount(String message) { for (String key : webSocketSet.keySet()) { try { if (key.equals(id)) { continue; } webSocketSet.get(key).sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * 发送信息给所有人 */ public void sendToAll(String message) throws IOException { for (String key : webSocketSet.keySet()) { try { webSocketSet.get(key).sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } public synchronized boolean pair() throws IOException { //是否存在等待匹配的用户 if (webSocketLiveList.size() > 0) { //随机匹配一个 Random ra = new Random(); int nextInt = ra.nextInt(webSocketLiveList.size()); toUser = webSocketLiveList.get(nextInt); try { ChatRandomServer UserId = webSocketSet.get(toUser); UserId.setToUser(id); sendToUser(session, "配对成功", toUser); } catch (IOException e) { e.printStackTrace(); } webSocketLiveList.remove(nextInt); return true; } //没有匹配的,则将自己加入等待匹配队列 webSocketLiveList.add(id); return false; } 复制代码
}
4. 前端支持
start: function () {
if (typeof (WebSocket) === "undefined") {
alert("您的浏览器不支持socket")
} else {
// 实例化socket
this.socket = new WebSocket(`ws://localhost:8082/websocket/room`);
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket消息
this.socket.onmessage = this.getMessage
this.socket.onclose = this.close
}
},
open: function () {
},
error: function () {
},
getMessage: function (obj) {
//接收信息后根据不同情况不同处理方式
let data = JSON.parse(obj.data);
if (data.code === 1) {
} else if (data.code === 2) {
} else {
}
},
close: function (e) {
},
doSend: function () {
if (that.sendData === '') {
return;
}
this.socket.send(that.sendData);
that.sendData = '';
},
复制代码
以上代码不完整,如果需要看下完整代码,联系我。