创建项目
这里使用阿里云服务器
https://start.aliyun.com/
勾选
- MyBatis Framework (在SQL分类下)
- MySQL Driver (在SQL分类下)
- WebSocket (在Messaging分类下)
- Spring Web (在Web分类下)
项目结构
消息发送机制
按照当前已有的知识,主要是HTTP HTTP自身是难以实现这种消息推送效果的~~
HTTP要想实现类似的效果,就需要基于“轮询”的机制~~
消息推送机制--服务器发送事件 (SSE)
我之前学习过的服务器开发,主要是这样的模型:
客户端,主动向服务器发起请求,服务器收到之后,返回一个响应。
如果客户端不主动发起请求,服务器是不能主动联系客户端的~~
这就是消息推送机制
轮询机制
判断轮训or消息推送
很明显,像这样的轮询操作,开销是比较大的,成本也是比较高的~~
- 如果轮询间隔时间长,玩家1落子之后,玩家2不能及时的拿到结果.
- 如果轮询间隔时间短,虽然即时性得到改善,但是玩家2不得不浪费更多的机器资源(尤其是带宽)
这就类似于去餐馆吃饭~~
1.每隔1分钟,就去前台看一眼,问问老板,,我的饭好了没~~
2.我直接找个角落坐下来,玩手机~~啥时候饭做好了,老板就端过来了~~
明显方案2更好,方案2就是服务器发送事件 (SSE)
但是缺点是只支持单向通信:SSE 只支持从服务器到客户端的单向通信,不能用于双向实时通信。
因此, websocket就是实现消息推送的一个主要的方式~~
websocket介绍
需要注意的是ws => websocket,不是"猥琐”的缩写~ (滑稽)
websocket报文格式
是一个应用层协议, 下层是基于TCP的
十分显而易见, 文本帧就是传输文本数据(比如JSON格式), 二进制帧就是传输二进制数据(比如图片、文件、音频流等)
提示一下, 这里Ping帧和Pong帧通常被用作心跳包,用于检测连接的状态,确保客户端和服务器之间的连接是活跃的。
- Ping帧:由一端(通常是服务器)发送,表示一个心跳请求。
- Pong帧:由接收到Ping帧的另一端(通常是客户端)发送,作为回应。
通过定期发送Ping帧并接收Pong帧,可以检测到连接是否正常以及是否需要重新建立连接。
Payload len: 当前数据报文携带的数据载荷长度。
- 这个字段本身就是一个变长的,WebSocket数据报能够承载的载荷长度是非常长的。
- Payload data: 实际的传输数据。
websocket握手过程
websocket握手过程(建立连接的过程)
使用网页端,尝试和服务器建立websocket连接.
网页端会先给服务器发起一个HTTP请求~~这个HTTP请求中会带有特殊的header
Connection: Upgrade
Upgrade: Websocket
这两个header 其实就是在告知服务器,我们要进行协议升级,
如果服务器支持 websocket,就会返回一个特殊的HTTP响应~~这个响应的状态码是101.(切换协议)
客户端和服务器之间就开始使用 websocket来进行通信了~~
编写一个简单的websocket代码
编写服务器(Java)
TestAPI extends这个类
public class TestAPI extends TextWebSocketHandler {
看看可以重写哪些方法
重写这四个方法
每个添加打印到控制台的信息
新建文件WebSocketConfig
TestAPI加上注解@Component
编写客户端(JS)
前后端函数都是一一对应, 只不过函数方法名字不一样, 意义都是一样的
js完整代码:
<script>// 创建websocket实例let websocket = new WebSocket("ws://127.0.0.1:8080/test");//给实例挂载一些回调函数websocket.onopen = function () {console.log("连接建立");}websocket.onclose = function () {console.log("连接关闭");}websocket.onerror = function () {console.log("出现错误");}websocket.onmessage = function (e) {console.log("收到消息 "+ e.data);}//实现点击按钮后, 通过websocket发送请求//querySelector是id选择器let input = document.querySelector("#message");let button = document.querySelector("#submit");button.onclick = function () {console.log("发送消息: "+input.value);websocket.send(input.value);}</script>
演示
访问 http://127.0.0.1:8080/TestAPI.html
连接建立
发送消息
那么如何实现服务器端也发送消息呢
@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {System.out.println("收到消息 "+ message.getPayload());session.sendMessage(message);System.out.println("发送消息 " +message.getPayload());}
只需要在
重启服务
至此, WebSocket示例已经完成