287人参与 • 2024-08-01 • html5
本系列的上篇文章《知识点扫盲 · 学会 webservice》介绍了企业开发中webservice
技术的实际应用,这边继续介绍一下websocket
的基础应用,希望可以帮助到大家。
这里先介绍实战运用,深入的部分后续专题介绍,让我们开始!
1、websocket 是 html5 开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。
2、websocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
3、websocket api中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
4、虽然前后端都可以互相推数据,但主要还是后端推送给前端,常见运用场景:实时聊天、通知公告、视频弹幕。
open socket.onopen 连接建立时触发
message socket.onmessage 客户端接收服务端数据时触发
error socket.onerror 通信发生错误时触发
close socket.onclose 连接关闭时触发
step1、引入依赖
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-websocket</artifactid>
</dependency>
step2、添加配置类
@configuration
public class websocketconfig {
/**
* 注入serverendpointexporter,
* 这个bean会自动注册使用了@serverendpoint注解声明的websocket endpoint
*/
@bean
public serverendpointexporter serverendpointexporter() {
return new serverendpointexporter();
}
}
step3、添加具体socket服务
@component
@slf4j
@serverendpoint("/websocket/{userid}")
public class websockethandle {
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private session session;
/**
* 用户id
*/
private string userid;
/**
* concurrent包的线程安全set,用来存放每个客户端对应的mywebsocket对象。
* 虽然@component默认是单例模式的,但sb还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
*/
private static final copyonwritearrayset<websockethandle> web_sockets = new copyonwritearrayset<>();
/**
* 用来存在线连接用户信息
*/
private static final concurrenthashmap<string, session> session_pool = new concurrenthashmap<>();
@override
public boolean equals(object o) {
if (this == o) {
return true;
}
if (o == null || getclass() != o.getclass()) {
return false;
}
websockethandle that = (websockethandle) o;
return objects.equals(session, that.session) && objects.equals(userid, that.userid);
}
@override
public int hashcode() {
return objects.hash(session, userid);
}
/**
* 链接成功调用的方法
*/
@onopen
public void onopen(session session, @pathparam(value = "userid") string userid) {
//初始化设置当前实例的信息
this.session = session;
this.userid = userid;
//加入全局socket管理(set)
web_sockets.add(this);
//加入全局session管理(map)
session_pool.put(userid, session);
log.info("websocket有新的连接,用户为:{}, 总数为:{}", userid, web_sockets.size());
}
/**
* 链接关闭调用的方法
*/
@onclose
public void onclose(session session) {
web_sockets.remove(this);
session_pool.remove(this.userid);
log.info("websocket有连接断开,用户为:{}, 总数为:{}", userid, web_sockets.size());
}
/**
* 收到客户端消息后调用的方法
*/
@onmessage
public void onmessage(session session, string message) {
log.info("websocket收到客户端消息,用户为:{}, 消息为:{}:", this.userid, message);
}
/**
* 发送错误时的处理
*/
@onerror
public void onerror(session session, throwable error) {
log.error("用户错误,原因:" + error.getmessage());
error.printstacktrace();
}
/**
* 发消息给全部人
*/
public void sendallmessage(string message) {
log.info("【websocket消息】广播消息:" + message);
for (websockethandle websocket : web_sockets) {
try {
if (websocket.session.isopen()) {
websocket.session.getasyncremote()
.sendtext(message);
}
} catch (exception e) {
e.printstacktrace();
}
}
}
/**
* 发消息给单个人
*/
public void sendonemessage(string userid, string message) {
session session = session_pool.get(userid);
if (session != null && session.isopen()) {
try {
log.info("【websocket消息】 单点消息:" + message);
session.getasyncremote()
.sendtext(message);
} catch (exception e) {
e.printstacktrace();
}
}
}
/**
* 发消息给多个人
*/
public void sendmoremessage(string[] userids, string message) {
for (string userid : userids) {
session session = session_pool.get(userid);
if (session != null && session.isopen()) {
try {
log.info("【websocket消息】 单点消息:" + message);
session.getasyncremote()
.sendtext(message);
} catch (exception e) {
e.printstacktrace();
}
}
}
}
}
step4、测试服务端效果
参考:websocket在线测试
按上述步骤改造完,就可以得到ws的服务端,地址形如:ws://127.0.0.1:8180/websocket/123
如果想不写客户端,可以直接使用测试网站进行测试,如下图。
step5、测试发送消息
随便写一个接口,就可以触发调用了,看看客户端是否有接收到。
注意,这里的websockethandle确实是单例的,但是单个socket连接也是存在的,而且bean实例里面的static类型的map和set还是存了后续想要操作的内容。
@requestmapping("/sockettest")
public void sockettest(string id) throws exception {
websockethandle.sendonemessage(id, "hello~");
}
前端使用websocket,可以用原生的方式,参考如下。
当然,也可以引用第三方库,比较出名的是socket.io。
/**
* 备忘
* 页面地址:http://localhost:8180/test/socketdemo.html
* 后端发消息:http://localhost:8180/sockettest?id=123
*/
if ("websocket" in window) {
let $scope = {}
let wsurl = "ws://127.0.0.1:8180/websocket/123"
let ws;
let tt;
let lockreconnect = false;
// 创建ws链接
$scope.createwebsocket = function (wsurl) {
try {
ws = new websocket(wsurl);
$scope.websocketinit();
} catch (e) {
lockreconnect = false
$scope.websocketreconnect(wsurl)//重连函数
}
};
// 初始化ws的方法
$scope.websocketinit = function () {
ws.onclose = function (error) {
//连接关闭的回调函数,进行重连
console.log("连接已关闭...", error);
$scope.websocketreconnect(wsurl)
};
ws.onerror = function (error) {
//连接错误的回调函数,进行重连
console.log("连接错误...", error);
$scope.websocketreconnect(wsurl)
};
ws.onopen = function () {//连接建立
//发一个初始化连接消息
ws.send('初始化连接');
//启动心跳检测
$scope.heartcheck.start();
};
ws.onmessage = function (event) {
if(event.data !== 'pong'){
let $test = $('#texta')
let temp = $test.text();
$test.text(temp + event.data + "\r\n");
console.log("收到后端的消息:", event.data);
} else {
console.log('收到pong消息,连接还正常~')
}
//接收一次后台推送的消息,即进行一次心跳检测重置
$scope.heartcheck.reset();
};
};
$scope.websocketreconnect = function (url) {
console.log("socket 连接断开,正在尝试重新建立连接");
//todo 下面这段代码会导致只重连一次,后续改进了再开放
//todo 长时间如果没收到pong消息应该也要处理,提示一下报错之类的
/*if (lockreconnect) {
return;
}
lockreconnect = true;*/
//没连接上会一直重连,设置延迟,避免请求过多
tt && cleartimeout(tt);
tt = settimeout(function () {
$scope.createwebsocket(url);
}, 4000)
};
//心跳检测
//onopen连接上,就开始start及时,如果在定时时间范围内,onmessage获取到了服务端消息,就重置reset倒计时,距离上次从后端获取消息30秒后,执行心跳检测,看是不是断了。
$scope.heartcheck = {
timeout: 5000, //默认30秒
timeoutobj: null,
reset: function () { //接收成功一次推送,就将心跳检测的倒计时重置为30秒
cleartimeout(this.timeoutobj);//重置倒计时
this.start();
},
start: function () {//启动心跳检测机制,设置倒计时30秒一次
this.timeoutobj = settimeout(function () {
//启动心跳
ws.send("ping");
}, this.timeout)
}
};
//开始创建websocket连接
$scope.createwebsocket(wsurl);
// 点击发消息给后端,后端收到后回复信息
function sendmessage() {
let message = document.getelementbyid("messageinput").value;
if (message) {
ws.send(message);
}
}
} else {
// 浏览器不支持 websocket
alert("您的浏览器不支持 websocket!");
}
此篇文章介绍了websocket
的基础应用,仅供学习参考。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论