springboot webSocket实现群聊/单聊
webSocket简介:
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
代码:
1.maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--下面两个事非必须的,我这里用thymeleaf管理前端页面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.WebSocket配置类,注入ServerEndpointExporter
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.实现群聊或者单聊,其实就是要让某个固定的客户端收发(个人粗鄙理解,不知道对不对),所以新建个实体类,用来接收并判断发送机制
public class MessageSok {
private int type;//单发/群发
private String sender;//发送
private String geter;//接收
private String msg;//信息
get/set方法
}
4.WebSocket服务(个人叫法)
@ServerEndpoint("/websocket/{name}")
@RestController
public class WebSocketServer {
//存储客户端的连接对象,每个客户端连接都会产生一个连接对象
// private static ConcurrentHashMap<String,WebSocketServer> map=new ConcurrentHashMap();
private static CopyOnWriteArraySet<WebSocketServer> socketSet = new CopyOnWriteArraySet<WebSocketServer>();
private static Map<String,Session> mapsok = new HashMap<String, Session>();
//每个连接都会有自己的会话
private Session session;
private String name;
@OnOpen
public void open(@PathParam("name") String name, Session session){
this.session=session;
this.name=name;
mapsok.put(session.getId(),session);
socketSet.add(this);
System.out.println(session.getId());
System.out.println("有新连接加入:"+name+",当前在线人数为" + socketSet.size());
this.session.getAsyncRemote().sendText("提示:"+name+"成功连接上WebSocket(其频道号:"+session.getId()+")当前在线人数为:"+socketSet.size());
// System.out.println(name+"连接服务器成功");
// System.out.println("客户端连接个数:"+getConnetNum());
}
@OnClose
public void close(){
socketSet.remove(name);
System.out.println(name+"断开了服务器连接");
}
@OnError
public void error(Throwable error){
error.printStackTrace();
System.out.println(name+"出现了异常");
}
@OnMessage
public void getMessage(String message) throws IOException {
System.out.println("来自客户端的消息-->"+name+": " + message);
//从客户端传过来的数据是json数据,所以这里使用jackson进行转换为SocketMsg对象,
// 然后通过socketMsg的type进行判断是单聊还是群聊,进行相应的处理:
//这里借鉴了https://blog.csdn.net/qq_41603102/article/details/82492040这位大神的博客
ObjectMapper objectMapper = new ObjectMapper();
MessageSok messageSok;
try {
messageSok = objectMapper.readValue(message, MessageSok.class);
if(messageSok.getType() == 1){
//单聊.需要找到发送者和接受者.
messageSok.setSender(session.getId());//发送者.
Session sendSession = mapsok.get(messageSok.getSender());
Session getSession = mapsok.get(messageSok.getGeter());
//发送给接受者.
if(getSession != null){
//收者发送,个人觉得自己就不用收了
getSession.getAsyncRemote().sendText(name+":"+messageSok.getMsg());
}else{
//发送给发送者.
sendSession.getAsyncRemote().sendText("系统消息:对方不在线或者您输入的频道号不对");
}
}else{
//群发消息
broadcast(name+": "+messageSok.getMsg());
}
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void broadcast(String message) {
for (WebSocketServer item : socketSet) {
//this.session.getBasicRemote().sendText(message);
//这里是群发,但是给自己发是不对的,所以如果是本身就不发
if(!item.session.getId().equals(session.getId())){
item.session.getAsyncRemote().sendText(message);//异步发送消息.
}
}
}
}
5.html页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>老年唠嗑室</title>
<style type="text/css">
.box{
width:300px;
height:500px;
border:1px solid blue;
margin:0 auto;
}
.inTer{
width:280px;
height:280px;
border:1px solid deeppink;
margin:0 auto;
margin-top:10px;
overflow-y:auto;
}
textarea{
display:block;
width: 276px;
height:65px;
margin:0 auto;
margin-top:5px;
}
#btn{
display:block;
float:right;
margin-right:10px;
margin-top:5px;
}
p{
display:inline-block;
border-radius:5px;
background:#dcdcdc;
font-size:12px;
padding:5px 5px;
margin:5px 0;
margin-left:5px;
max-width:140px;
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="box">
用户名: <input type="text" name="name" id="usname">
频道号:<input id="geter" type="text" />
<button onclick="conectWebSocket()">连接WebSocket</button>
<div class="inTer" id="father">
</div>
<textarea style="resize: none;" id="txt">
</textarea>
<input type="button" id="btn" value="发送" />
</div>
</body>
</html>
<script>
var nickname=null;
var btn = document.getElementById("btn");
var txt = document.getElementById("txt");
var father = document.getElementById("father")
var p = document.getElementsByTagName("p");
var websocket=null;
function conectWebSocket() {
nickname=document.getElementById("usname").value;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws:3w19699285.goho.co/websocket/"+nickname);
} else {
alert("浏览器不支持");
}
// if('WebSocket' in window){
// websocket=new WebSocket("ws:192.168.101.103:8082/websocket/"+name);
// }else {
//
//
// }
websocket.onopen = function () {
console.log("webscoket已经连接成功");
addMessage("webscoket已经连接成功");
};
websocket.onclose = function () {
console.log("webscoket连接失败");
addMessage("webscoket连接失败");
};
websocket.onmessage = function (event) {
addMessage(event.data);
};
websocket.onerror = function () {
console.log("webscoket连接失败");
addMessage("webscoket连接失败");
};
}
btn.onclick=function(){
if(txt.value==""){
alert("请勿发送空内容");
}
else{
var son = document.createElement("p");
son.style.backgroundColor="yellowgreen";
son.style.clear="both";
son.style.float="right";
son.style.marginRight="5px";
son.innerText=txt.value;
// websocket.send(nickname+":"+txt.value);
father.appendChild(son);
send(txt.value);
txt.value="";
son.scrollIntoView();
}
}
document.onkeydown=function(evt){
var e = evt || event;
e.keyCode=e.which=e.charCode;
if(e.keyCode==13 || e.keyCode==10){
if(txt.value==""){
alert("请勿发送空内容");
}
else{
var son = document.createElement("p");
son.style.backgroundColor="yellowgreen";
son.style.clear="both";
son.style.float="right";
son.style.marginRight="5px";
son.innerText=txt.value;
// websocket.send(nickname+":"+txt.value);
send(txt.value);
father.appendChild(son);
txt.value="";
son.scrollIntoView();
}
}
}
function addMessage(message) {
var son = document.createElement("p");
son.style.backgroundColor="yellowgreen";
son.style.clear="both";
son.style.float="left";
son.style.marginRight="5px";
son.innerText=message;
father.appendChild(son);
son.scrollIntoView();
}
function send(messagetxt) {
//获取输入的文本信息进行发送
var message = messagetxt;
var geter = document.getElementById('geter').value;
var messageSoc = {msg:message,geter:geter};
if(geter == ''){
//群聊.
messageSoc.type = 0;
}else{
//单聊.
messageSoc.type = 1;
}
websocket.send(JSON.stringify(messageSoc));
}
</script>
这里是先连接,然后试试群发
楷哥发,除了楷哥其他人都接收
然后私发:
阳仔问候楷哥,楷哥回应,需要给对应的频道号,这里是sessionid,然后结果,东哥是没有消息的:
结语
其实实现websocket的方式还有很多,比如还有订阅主题什么的,还有通过security方式管理用户,大家可以做个了结