一、websocket简介
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。(百度百科)
二、多人聊天室的实现
1、后端服务器
(1)后端使用springboot搭建服务器,在IDEA中新建一个springboot工程,在pom.xml文件中引入spring-boot-starter-websocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
(2)写一个配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
(3)controller
package com.joe.study.eshopdemo.controller;
import org.springframework.stereotype.Controller;
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;
@Controller
@ServerEndpoint("/groupChat/{roomId}/{username}")
public class GroupChatController {
// 保存 聊天室id -> 聊天室成员 的映射关系
private static ConcurrentHashMap<String, List<Session>> rooms = new ConcurrentHashMap<>();
// 收到消息调用的方法,群成员发送消息
@OnMessage
public void onMessage(@PathParam("roomId") String roomId,
@PathParam("username") String username, String message) {
List<Session> sessionList = rooms.get(roomId);
// 分别向聊天室的成员发送消息
sessionList.forEach(item -> {
try {
String text = username + ": " + message;
item.getBasicRemote().sendText(text);
} catch (IOException e) {
e.printStackTrace();
}
});
}
// 建立连接调用的方法,群成员加入
@OnOpen
public void onOpen(Session session, @PathParam("roomId") String roomId) {
List<Session> sessionList = rooms.get(roomId);
if (sessionList == null) {
sessionList = new ArrayList<>();
rooms.put(roomId, sessionList);
}
sessionList.add(session);
System.out.println("成员加入---- 聊天室号:" + roomId + " 当前聊天室人数:" + sessionList.size());
}
// 关闭连接调用的方法,群成员退出
@OnClose
public void onClose(Session session, @PathParam("roomId") String roomId) {
List<Session> sessionList = rooms.get(roomId);
sessionList.remove(session);
System.out.println("成员退出---- 聊天室号:" + roomId + " 当前聊天室人数:" + sessionList.size());
}
// 传输消息错误调用的方法
@OnError
public void OnError(Throwable error) {
System.out.println("消息传递出错");
}
}
2、前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>websocket多人聊天室</title>
<style>
.container {
margin-top: 30px;
margin-left: 30px;
}
.span1 {
display: inline-block;
width: 80px;
font-size: 13px;
}
input {
width: 200px;
height: 30px;
outline: none;
border: 1px solid #3998f7;
border-radius: 3px;
padding: 0 10px;
}
input:hover,
textarea:hover {
box-shadow: 0 0 3px #3998f7;
}
#name {
margin-top: 15px;
}
button {
width: 100px;
height: 30px;
outline: none;
color: #fff;
font-size: 13px;
font-weight: bold;
background-color: #168ed1;
border: none;
border-radius: 5px;
cursor: pointer;
}
#connectBtn,
#content,
#sendBtn,
#closeBtn {
margin-top: 10px;
}
#messages {
margin-top: 20px;
}
textarea {
width: 300px;
height: 80px;
outline: none;
border: 1px solid #3998f7;
border-radius: 3px;
color: rgba(0, 0, 0, 0.7);
font-size: 13px;
font-weight: bold;
padding: 5px;
}
#messages, .me, .name {
color: rgba(0, 0, 0, 0.7);
font-size: 14px;
font-weight: bold;
}
.me {
color: red;
}
.name {
color: royalblue;
}
</style>
</head>
<body>
<div class="container">
<span class="span1">聊天室id:</span><input type="text" id="roomId" placeholder="请输入聊天室id" /><br />
<span class="span1">昵称:</span><input type="text" id="name" placeholder="请输入你的昵称" /><br />
<button id="connectBtn">连接</button><br />
<textarea id="content" placeholder="请输入要发送的消息"></textarea><br />
<button id="sendBtn">发送</button>
<button id="closeBtn">退出</button><br />
<div id="messages"></div>
</div>
<script type="text/javascript">
const connectBtn = document.getElementById("connectBtn");
const sendBtn = document.getElementById("sendBtn");
const closeBtn = document.getElementById("closeBtn");
const roomIdDOM = document.getElementById("roomId");
const nameDOM = document.getElementById("name");
const messagesDOM = document.getElementById("messages");
const contentDOM = document.getElementById("content");
let webSocket = null;
// 加入聊天室
connectBtn.addEventListener("click", () => {
const roomId = roomIdDOM.value;
const name = nameDOM.value;
if (!roomId || !name) {
alert("房间号和用户名不能为空");
return;
}
const url = "ws://localhost:8787/groupChat/" + roomId + "/" + name;
webSocket = new WebSocket(url);
webSocket.onerror = e => {
alert("出错了");
};
webSocket.onopen = e => {
messagesDOM.innerHTML = "您已加入聊天室!";
connectBtn.style.display = 'none'
};
webSocket.onmessage = e => {
const nickname = e.data.split(":")[0];
const data = e.data.slice(e.data.indexOf(':') + 1)
messagesDOM.innerHTML += `<br /><span class='${
nickname === name ? "me" : "name"
}'>${nickname === name ? "我" : nickname}: </span>  ${data}`;
};
webSocket.onclose = e => {
connectBtn.style.display = 'none'
alert("您已退出聊天室");
};
});
// 发送消息
sendBtn.addEventListener("click", () => {
webSocket.send(contentDOM.value);
contentDOM.value = "";
});
// 退出聊天室
closeBtn.addEventListener("click", () => {
webSocket.close();
});
</script>
</body>
</html>
3、效果