目录
新建component文件夹,加入WebSocketServer
WebSocket 是基于TCP/IP协议,独立于HTTP协议的通信协议。
WebSocket 是双向通讯,有状态,客户端一(多)个与服务端一(多)双向实时响应(客户端 ⇄ 服务端)。
WebSocket 是应用在浏览器的 Socket (是 Socket 模型接口的实现),Socket 是一个网络通信接口 (通信规范)。
添加依赖
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
添加WebSocketConfig配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
新建component文件夹,加入WebSocketServer
package com.example.pipayshopapi.component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author websocket服务
*/
@ServerEndpoint(value = "/imserver/{userId}")
@Component
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
// 注入查看聊天列表的服务
private WebSocketLook webSocketLook=WebSocketLook.getInstance();
/**
* 记录当前在线连接数
*/
public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
// 保存当前用户session
sessionMap.put(userId, session);
System.out.println(sessionMap.size());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session, @PathParam("userId") String userId) {
// 移除当前用户session
sessionMap.remove(userId);
}
/**
* 收到客户端消息后调用的方法
* 后台收到客户端发送过来的消息
* onMessage 是一个消息的中转站
* 接受 浏览器端 socket.send 发送过来的 json数据
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
JSONObject obj = JSON.parseObject(message);
String toUserId = obj.getString("to"); // to表示发送给哪个用户,比如 admin
String text = obj.getString("text"); // 发送的消息文本 hello
Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本
if (toSession != null) {
// 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
// {"from": "zhang", "text": "hello"}
JSONObject jsonObject = new JSONObject();
jsonObject.put("from", userId); // from 是 zhang
jsonObject.put("text", text); // text 同上面的text
this.sendMessage(jsonObject.toString(), toSession);
log.info("发送给用户username={},消息:{}", toUserId, jsonObject.toString());
} else {
// 发送消息给可能在聊天室外面的人
webSocketLook.sendMsgLook(userId,toUserId,text);
}
// 存入数据库
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 服务端发送消息给客户端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
}
加入WebSocketLook
package com.example.pipayshopapi.component;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author websocket服务
*/
@ServerEndpoint(value = "/imlook/{userId}")
@Component
public class WebSocketLook {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
private static WebSocketLook webSockerLook = null;
/**
* 记录当前在线连接数
*/
public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
public static WebSocketLook getInstance() {
if (webSockerLook == null) {
webSockerLook = new WebSocketLook();
}
return webSockerLook;
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
// 保存当前用户session
sessionMap.put(userId, session);
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session, @PathParam("userId") String userId) {
// 移除当前session
sessionMap.remove(userId);
}
/**
* 收到客户端消息后调用的方法
* 后台收到客户端发送过来的消息
* onMessage 是一个消息的中转站
* 接受 浏览器端 socket.send 发送过来的 json数据
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 发送消息给聊天室外面的人
* @param userId
* @param toUserId
* @param text
*/
public void sendMsgLook(String userId, String toUserId, String text) {
// 判断是否接收方是否在线并且在聊天室外
Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本
// 接收方在聊天室外
if (toSession != null) {
// 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
// {"from": "zhang", "text": "hello"}
JSONObject jsonObject = new JSONObject();
jsonObject.put("from", userId); // from 是 zhang
jsonObject.put("text", text); // text 同上面的text
jsonObject.put("to",toUserId); // 接收方
this.sendMessage(jsonObject.toString(), toSession);
log.info("发送给用户username={},消息:{}", toUserId, jsonObject.toString());
}
// 接收方离线中,不操作,
}
/**
* 服务端发送消息给客户端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
}
chat.html核心js代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>chat</title>
</head>
<body>
<div id="app">
<input v-model="userId">
<button @click="run">点击进入连接chatList</button>
<input v-model="text">
<input v-model="toUserId">
<button @click="send">发送消息</button>
</div>
</body>
</html>
<script>
let socket;
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
userId:"",
text:"",
toUserId:"",
},
methods:{
run:function(){
console.log(this.userId);
// 连接websocker
let userId = this.userId;
let _this = this;
if (typeof (WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
console.log("您的浏览器支持WebSocket");
let socketUrl = "ws://localhost:7125/api/imserver/" + userId;
if (socket != null) {
socket.close();
socket = null;
}
// 开启一个websocket服务
socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function () {
console.log("websocket已打开");
};
// 浏览器端收消息,获得从服务端发送过来的文本消息
socket.onmessage = function (msg) {
console.log("收到数据====" + msg.data)
let data = JSON.parse(msg.data) // 对收到的json数据进行解析, 类似这样的: {"users": [{"username": "zhang"},{ "username": "admin"}]}
};
//关闭事件
socket.onclose = function () {
console.log("websocket已关闭");
};
//发生了错误事件
socket.onerror = function () {
console.log("websocket发生了错误");
}
}
},
send:function(){
console.log(this.text);
console.log(this.toUserId);
if (typeof (WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
console.log("您的浏览器支持WebSocket");
// 组装待发送的消息 json
// {"from": "zhang", "to": "admin", "text": "聊天文本"}
let message = {from: this.userId, to: this.toUserId, text: this.text}
socket.send(JSON.stringify(message)); // 将组装好的json发送给服务端,由服务端进行转发
this.text = '';
}
}
}
})
</script>
chatList的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>chatList</title>
</head>
<body>
<div id="app">
<input v-model="userId">
<button @click="run">点击进入连接chatList</button>
</div>
</body>
</html>
<script>
let socket;
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
userId:""
},
methods:{
run:function(){
console.log(this.userId);
// 连接websocker
let userId = this.userId;
let _this = this;
if (typeof (WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
console.log("您的浏览器支持WebSocket");
let socketUrl = "ws://localhost:7125/api/imlook/" + userId;
if (socket != null) {
socket.close();
socket = null;
}
// 开启一个websocket服务
socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function () {
console.log("websocket已打开");
};
// 浏览器端收消息,获得从服务端发送过来的文本消息
socket.onmessage = function (msg) {
console.log("收到数据====" + msg.data)
let data = JSON.parse(msg.data) // 对收到的json数据进行解析, 类似这样的: {"users": [{"username": "zhang"},{ "username": "admin"}]}
};
//关闭事件
socket.onclose = function () {
console.log("websocket已关闭");
};
//发生了错误事件
socket.onerror = function () {
console.log("websocket发生了错误");
}
}
}
}
})
</script>
两个人在同一聊天室
发送信息给13
13接收到信息
12发送14,14不在聊天室里面
14收到12的信息