一、WebSocket概述
1、WebSocket简介
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
双全工通信:全双工通信又称为双向同时通信,即通信的双方可以同时发送和接收信息的信息交互方式。 半双工通信:数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输
1.1 轮询
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
所谓轮询(polling)其实是客户端按规定时间定时向服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) —- loop
1.2 长轮询
长轮询(Long Polling)是一种实现实时通信的技术,通常用于前端与后端服务器之间的交互。在传统的Ajax请求中,前端向后端发送请求并等待后端的响应,而长轮询则允许服务器在没有数据更新时不立即返回响应,而是一直保持连接,直到有新数据更新时再返回响应
场景再现:
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
二、为什么需要WebSocket
HTTP 是基于请求响应式的,即通信只能由客户端发起,服务端做出响应。
通过HTTP实现即时通讯,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源,因为必须不停连接,或者 HTTP 连接始终打开。
WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。
2.1 WebSocket特点:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信。 (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
三、SpringBoot整合netty-WebSocket
1.pom文件
//主要依赖
2. yaml文件
websocket:
port: 2222
path: /api/websocket
3. 配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
4.服务类
package com.my.websocket;
import com.alibaba.fastjson.JSONObject;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.yeauty.annotation.*;
import org.yeauty.pojo.ParameterMap;
import org.yeauty.pojo.Session;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
@ServerEndpoint(prefix = "websocket")//标注ws服务的路径
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
public static final Map
@OnOpen
public void onOpen(Session session, HttpHeaders headers, ParameterMap parameterMap) throws IOException {
String userId = parameterMap.getParameter("userId");
sessionMap.put(userId,session);
System.out.println("新用户连接===================>" + userId);
for (Map.Entry
sendAllMessage(JSONObject.toJSONString(sessionMap.keySet()));
}
}
@OnClose
public void onClose(Session session) throws IOException {
System.out.println("one connection closed");
try{
for (Map.Entry
if(entry.getValue()==session){
sessionMap.remove(entry.getKey());
}
}
}catch (Exception e){
System.out.println(e);
}
}
@OnError
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
}
@OnMessage
public void onMessage(Session session, String message) {
Map
if(map.get("invitee")!=null){
String invitee = map.get("invitee").toString();
for (Map.Entry
if(entry.getKey().equals(invitee)){
sendMessage(message,entry.getValue());
}
}
}else {
String to = map.get("to").toString();
for (Map.Entry
if(entry.getKey().equals(to)){
sendMessage(message,entry.getValue());
}
}
}
}
private void sendMessage(String message, Session toSession) {
try {
log.info("服务端给客户端[{}]发送消息{}", toSession.id(), message);
System.out.println(message);
toSession.sendText(message);
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
/**
* 服务端发送消息给所有客户端
*/
public static void sendAllMessage(String message) {
try {
for (Session session : sessionMap.values()) {
log.info("服务端给客户端[{}]发送消息{}", session.id(), message);
session.sendText(message);
}
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
}
四、前端代码
//创建链接
this.socket = new WebSocket(
"ws://qingshan.natapp1.cc/api/websocket?userId=" + this.user.id
);
//初始化方法
init() {
let _this = this;
//打开事件
this.socket.onopen = function () {
console.log("websocket已打开");
};
// 浏览器端收消息,获得从服务端发送过来的文本消息
this.socket.onmessage = function (msg) {
console.log("收到数据====" + msg.data);
if (!_this.chatUser) {
return;
}
let data = JSON.parse(msg.data);
if (data.promoter != null) {
_this.getFriendReq();
} else {
if (data.from == _this.chatUser.id && data.from) {
let message = {
senderId: data.from,
receiverId: _this.user.id,
time: data.time,
info: data.text,
};
_this.chatinfos.push(message);
} else {
_this.userRefList.forEach(function (f) {
if (f.user.id == data.from) {
f.info.info = data.text;
f.info.time = data.time;
f.unReadCount = f.unReadCount + 1;
}
});
}
}
};
//关闭事件
this.socket.onclose = function () {
console.log("websocket已关闭");
_this.socket = new WebSocket(
"ws://qingshan.natapp1.cc/api/websocket?userId=" + _this.user.id
);
_this.init();
};
//发生了错误事件
this.socket.onerror = function () {
console.log("websocket发生了错误,尝试重连");
};
},
//发送消息
send() {
let headers = {
token: this.token,
};
let _this = this;
if (!this.chatUser) {
this.$message({ type: "warning", message: "请选择聊天对象" });
return;
}
if (!this.text) {
this.$message({ type: "warning", message: "请输入内容" });
} else {
// 组装待发送的消息 json
// {"from": "zhang", "to": "admin", "text": "聊天文本"}
let chatInfo = {
from: this.user.id,
to: this.chatUser.id,
text: this.text,
};
this.socket.send(JSON.stringify(chatInfo)); // 将组装好的json发送给服务端,由服务端进行转发
let message = {
senderId: _this.user.id,
receiverId: _this.chatUser.id,
time: new Date(),
info: this.text,
isEnable: 1,
};
let url = "http://19174933852.gnway.cc:8000/api/message/sendInfo";
this.$axios
.post(url, message, { headers })
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
_this.chatinfos.push(message);
document.getElementsByClassName("chatBox")[0].scrollTop =
document.getElementsByClassName("chatBox")[0].scrollHeight;
this.text = "";
}
},
相关文章
发表评论