首先服务端的集成,引入依赖包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<netty-socketio.version>1.7.16</netty-socketio.version>
</properties>
<dependencies>
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>${netty-socketio.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-websocket-socketio</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
服务端中websocket主要代码
@OnConnect
public void onConnect(SocketIOClient client) {
if (client != null) {
String token = client.getHandshakeData().getSingleUrlParam("token");
// 模拟用户id 和token一致
String userId = client.getHandshakeData().getSingleUrlParam("token");
UUID sessionId = client.getSessionId();
dbTemplate.save(userId, sessionId);
log.info("连接成功,【token】= {},【sessionId】= {}", token, sessionId);
} else {
log.error("客户端为空");
}
}
/**
* 添加disconnect事件,客户端断开连接时调用,刷新客户端信息
*
* @param client 客户端对象
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
if (client != null) {
String token = client.getHandshakeData().getSingleUrlParam("token");
// 模拟用户id 和token一致
String userId = client.getHandshakeData().getSingleUrlParam("token");
UUID sessionId = client.getSessionId();
dbTemplate.deleteByUserId(userId);
log.info("客户端断开连接,【token】= {},【sessionId】= {}", token, sessionId);
client.disconnect();
} else {
log.error("客户端为空");
}
}
/**
* 加入群聊
*
* @param client 客户端
* @param request 请求
* @param data 群聊
*/
@OnEvent(value = Event.JOIN)
public void onJoinEvent(SocketIOClient client, AckRequest request, JoinRequest data) {
log.info("用户:{} 已加入群聊:{}", data.getUserId(), data.getGroupId());
client.joinRoom(data.getGroupId());
server.getRoomOperations(data.getGroupId()).sendEvent(Event.JOIN, data);//触发 socket.on()函数 8081端口
}
@OnEvent(value = Event.CHAT)
public void onChatEvent(SocketIOClient client, AckRequest request, SingleMessageRequest data) {
Optional<UUID> toUser = dbTemplate.findByUserId(data.getToUid());
System.out.println(toUser);
if (toUser.isPresent()) {
log.info("用户 {} 刚刚私信了用户 {}:{}", data.getFromUid(), data.getToUid(), data.getMessage());
sendToSingle(toUser.get(), data);
request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); //返回客户端,发起请求的request-session 8080端口
} else {
request.sendAckData(Dict.create()
.set("flag", false)
.set("message", "发送失败,对方不想理你(" + data.getToUid() + "不在线)"));
}
}
@OnEvent(value = Event.GROUP)
public void onGroupEvent(SocketIOClient client, AckRequest request, GroupMessageRequest data) {
Collection<SocketIOClient> clients = server.getRoomOperations(data.getGroupId()).getClients();
boolean inGroup = false;
for (SocketIOClient socketIOClient : clients) {
if (ObjectUtil.equal(socketIOClient.getSessionId(), client.getSessionId())) {
inGroup = true;
break;
}
}
if (inGroup) {
log.info("群号 {} 收到来自 {} 的群聊消息:{}", data.getGroupId(), data.getFromUid(), data.getMessage());
sendToGroup(data);
} else {
request.sendAckData("请先加群!");
}
}
/**
* 单聊
*/
public void sendToSingle(UUID sessionId, SingleMessageRequest message) {
server.getClient(sessionId).sendEvent(Event.CHAT, message);
}
/**
* 广播
*/
public void sendToBroadcast(BroadcastMessageRequest message) {
log.info("系统紧急广播一条通知:{}", message.getMessage());
for (UUID clientId : dbTemplate.findAll()) {
if (server.getClient(clientId) == null) {
continue;
}
server.getClient(clientId).sendEvent(Event.BROADCAST, message);
}
}
/**
* 群聊
*/
public void sendToGroup(GroupMessageRequest message) {
server.getRoomOperations(message.getGroupId()).sendEvent(Event.GROUP, message);
}
前端主要代码
<script>
const token = 'user' + Math.floor((Math.random() * 1000) + 1);
const url = `http://127.0.0.1:8088?token=${token}`;
const socket = io.connect(url);
socket.on('connect', function () {
output(`<span class="connect-msg">系统通知: ${token}成功连接至websocket服务器</span>`);
});
socket.on('join', function (data) {
output(`<span class="sys-msg">${data.groupId} 群通知: 新人 ${data.userId} 请爆照</span>`);
});
socket.on('chat', function (data) {
output(`<span class="username-msg">系统通知: 收到来自 ${data.fromUid} 的悄悄话: ${data.message}</span>`);
});
socket.on('group', function (data) {
output(`<span class="username-msg">${data.groupId} 群消息: ${data.fromUid} 说: ${data.message}</span>`);
});
socket.on('disconnect', function () {
output(`<span class="disconnect-msg">系统通知: ${token}已从websocket服务器断开连接</span>`);
});
socket.on('broadcast', function (data) {
output(`<span class="broadcast">${data.message}</span>`);
});
function sendConnect() {
socket.connect();
}
function sendDisconnect() {
socket.disconnect();
}
function sendBroadcast() {
axios.post('/demo/send/broadcast', {
message: '系统广播通知: 当前时间 ' + moment().format('YYYY-MM-dd HH:mm:ss.SSS')
}).then((response) => {
const {flag, message} = response.data;
if (flag) {
layer.msg(message, {icon: 6});
} else {
layer.msg(message, {icon: 5});
}
});
}
function sendJoin() {
let joinRequest = {
userId: token,
groupId: "666"
};
socket.emit('join', joinRequest);
}
function sendGroup() {
let message = $('#msg').val();
if (message === '') {
layer.msg('你不说点什么嘛?', {icon: 5});
return;
}
$('#msg').val('');
let groupRequest = {
fromUid: token,
groupId: "666",
message: message
};
socket.emit('group', groupRequest, function (data) {
if (data) {
layer.msg(data, {icon: 5});
}
});
}
function sendChat() {
let toUserId = $('#to').val();
let message = $('#msg').val();
if (toUserId === '') {
layer.msg('请输入对方昵称', {icon: 5});
return;
}
if (message === '') {
layer.msg('你不说点什么嘛?', {icon: 5});
return;
}
$('#to').val('');
$('#msg').val('');
let singleRequest = {
fromUid: token,
toUid: toUserId,
message: message
};
socket.emit('chat', singleRequest, function (data) {
output(`<span class="username-msg">系统通知: 你刚刚和 ${singleRequest.toUid} 说了句悄悄话</span>`);
if (data && data.flag) {
output(`<span class="username-msg">系统通知: 悄悄话, ${data.message}</span>`);
} else {
output(`<span class="disconnect-msg">系统通知: 悄悄话, ${data.message}</span>`);
}
});
}
function output(message) {
let currentTime = "<span class='time'>" + moment().format('YYYY-MM-dd HH:mm:ss.SSS') + "</span>";
let element = $("<div>" + currentTime + " " + message + "</div>");
$('#console').prepend(element);
}
</script>
以上代码难点分析
服务端中,websocket一共分为两个端口去,服务端程序本身的端口是8080,一个是8081端口 用于websocket时间监听端口,例如发送消息事件,加入群聊事件,消息事件的触发也就是8081端口,而8080,即服务端端口主要负责传递数据,核心代码如下
request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); //返回客户端,发起请求的request-session 8080端口
server.getRoomOperations(data.getGroupId()).sendEvent(Event.JOIN, data);//触发 socket.on()函数 8081端口
前端代码中和后端一样,只用到一个端口8081,负责监听服务端的事件,核心结构为