如何快速实现一个聊天室?

前些天做了一个网站:modubox.cn 其中有个群聊插件,许多人问如何实现的。这里简单说下,为了快速完成群聊功能,我选择从最简单的 WebSocket 开始。

什么是WebSocket ?

既然要使用它,就需要了解一下它吧。WebSocket其实也是一种基于TCP的网络协议,它与HTTP协议最大的不同是:是一种双向通信协议,在建立连接后,WebSocket服务器端和客户端都能主动向对方发送或接收数据,而HTTP协议只能客户端主动发起通信。

所以WebSocket能够用于聊天,当然其他地方也能应用,如果做客服系统或推送消息都可以从这里开始。

如何实现单聊/群聊?

群聊:所有客户端的消息发送到服务器,服务端将消息发送给所有客户端。

单聊:WebSocket客户端之间是无法直接通信的,想要通信,必须由服务端转发。

群聊单聊

群聊单聊

实现

1. 引入WebSocket的支持

我们使用当前最流行的Spring Boot框架构建项目,然后引入Spring Boot 对 WebSocket 的支持:

1
2
3
4
xml复制代码<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 开启WebSocket

1
2
3
4
5
6
7
8
typescript复制代码@Configuration
public class WebSocketConfig {

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

3. 服务端

这里主要有以下几点:

  1. 声明服务端点路径
  2. 存储所有连接用户,等待匹配用户
  3. 连接 onOpen,消息OnMessage,关闭onClose,错误onError 方法
  4. 发送消息给特定连接者

@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 = “”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
scss复制代码/**
* 连接建立成功调用的方法
*/
@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. 前端支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
kotlin复制代码 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 = '';
},

以上代码不完整,如果需要看下完整代码,联系我。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%