如何快速实现一个聊天室?
码上实战
共 6386字,需浏览 13分钟
· 2021-08-26
如何快速实现一个聊天室?
聊天室前些天做了一个网站:https://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<String> 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 = '';
},
以上代码不完整,如果需要看下完整代码,联系我。
评论
一个朋友
一个朋友,在深圳奋斗7年,和女友在去年合力在龙华买了一套房,总价600万,首付3成。但就在昨天,他们崩溃了。深圳推出可售型人才住房,就在他们新房附近,同样面积,总价不到400万,售价近乎腰斩。他们想不明白,同样是深圳人,买房人为什么都要被当成炒房客对待?二手房冰封,卖不出,新房不断打着,像极了上世纪
嵌入式Linux
0
偷偷告诉你如何一台电脑开多个微信!
大家好,我是轩辕。前几天在粉丝群里,有人问我是怎么在一台电脑上同时登录两个微信的?正好之前写过一篇文章,分析过原理,分享给没看过的小伙伴学习一下。手机端多开微信估计很多人都知道,像华为、小米等手机系统都对此做了支持,不过在运行Windows系统的电脑上怎么启动两个微信呢?其实很简单,你只需要写一个批
编程技术宇宙
0
老爸嘲讽我了,写破代码一年就挣十几万,他在工地带50个工人,一个月光人头费就3万,让我滚回去跟他干!
点击上方 "大数据肌肉猿"关注, 星标一起成长点击下方链接,进入高质量学习交流群今日更新| 1052个转型案例分享-大数据交流群来自:网络,侵删有个网友的父亲是做工程的,天天就嘲笑他,说他天天写着破代码有啥用,一年就拿个十多万的死工资,然后告诉他自己在工地里面带了50个工人,一个月能抽三万
程序源代码
0
测试新人,如何快速上手一个陌生的系统!
大家好,我是狂师!作为刚入行不久的测试新人,面对一个陌生的系统时,可能会感到有些手足无措。面对一个全新的系统系统,如何快速上手并展开有效的测试工作是一个重要的挑战。本文将探讨测试新人如何通过一系列步骤和策略,快速熟悉并掌握新系统的测试要点,从而提高测试效率和质量。本文旨在为测试新手提供一份指导,帮助
测试开发技术
0
Windows格式化对话框是一个使用了30年的 “临时解决方案”
戴夫-普卢默(Dave Plummer)是微软的资深工程师,曾创造了任务管理器、Windows 弹球、原生 ZIP 支持(微软出钱买断该功能后,他用这笔钱购买了一辆红色克尔维特)等传奇。近日他在自己的 X 账户上分享了创建 "格式化" 对话框的故事 —— 称其是一个使用了长达 30 年的 “临时解决
开源Linux
0
光纤详解:光纤跳线如何分类,多向单模转换?
本文来自“光纤详解:光纤跳线如何分类,多向单模转换?”,光纤跳线作为光网络布线最基础的元件之一,被广泛应用于光纤链路的搭建中。如今,光纤制造商根据应用场景的不同推出众多类型的光纤跳线,如MPO/LC/SC/FC/ST光纤跳线,单工/双工光纤跳线,单模/多模光纤跳线等,它们之间各有特色,且不可替代。本
架构师技术联盟
0
如何计算数据中心的冷却需求?
今日分享 【导读】数据中心的冷却要求受多种因素影响,包括设备的热量输出、占地面积、设施设计和电气系统功率额定值等等……众所周知,环境因素会严重影响数据中心设备。过多的热量积聚会损坏服务器,可能导致其自动关闭。经常在高于可接受的温度下运行服务器会缩短其使用
数据中心运维管理
0
5000w+ 的大表如何拆?亿级别大表拆分实战复盘
前言笔者是在两年前接手公司的财务系统的开发和维护工作。在系统移交的初期,笔者和团队就发现,系统内有一张5000W+的大表。跟踪代码发现,该表是用于存储资金流水的表格,关联着众多功能点,同时也有众多的下游系统在使用这张表的数据。进一步的观察发现,这张表还在以每月600W+的数据持续增长,也就是说,不超
码农编程进阶笔记
0