一、【作用】
为了保持连接的可持续性和稳定性,websocket心跳就是解决这个问题的。
二、【剖析】
1、如果设备网络断开,原生websocket不会立即触发websocket任何事件,前端也无法得知当前连接是否已经断开。
2、我们使用websocket.send方法时,浏览器才会发现连接断开了。便会触发onclose方法。
3、同样后端websocket服务也可能造成连接断开,前端也不会收到断开的通知,因此需要前端定时发送心跳消息【ping】,后端收到ping类型的消息返回【pong】消息,告知前端连接正常。反之连接断开。
三、【原理】
以前端最为主动方,定时发送ping消息的方式就是浏览器心跳机制。
四、【websocket和socket区别】
websocket是H5一种新协议。它实现了浏览器与服务器双通信。建立握手动作还是需要借助http请求完成。
http协议是非持久化的,单向的网络协议。在建立连接连接后只允许浏览器发出请求,服务器才能返回相应的数据。浏览器不断的发送请求,而且请求的Header也比较长:浪费流量、服务器资源
即时通讯在网站上是很常见的,比如网页QQ等。之前通常采用的方式是轮询、Comet技术实现。
websocket就可以解决这一点。只需要服务器和浏览器通过http协议进行握手动作,然后单独建立一条tcp通信通道进行数据传送。
握手动作
浏览器、服务器建立tcp连接的三次握手,是通信的基础;
tcp连接成功后,浏览器通过http协议,向服务器传送websocket支持的版本信息;
服务器收到客户端的握手请求后,同样采用http洗衣反馈数据;
客户端收到连接成功消息后,以后采用tcp通道进行数据传输。
三次握手
第一次握手:客户端发数据给服务端,等待确认。
第二次握手:服务端收到数据,重新发确认数据到客户端确认连接请求。
第三次握手:客户端收到服务端确认,发数据给服务器告知可以建立连接了。
问:为什么是三次握手?
答:三次握手建立连接,是为了确保通信双方都具有收发数据的能力。
两次不安全,四次没必要。
四次挥手
第一次挥手,客户端发送关闭相关的数据到服务端,并进入等待状态;
第二次挥手,服务端收到关闭相关的数据后,给客户端发送确认序号,服务端进入关闭等待状态;
第三次挥手,服务端再次发送关闭server到client的相关数据到客户端,并进入LAST_ACK状态。
第四次挥手:客户端收到关闭server到client数据后,进入TIME_WAIT状态,接着发送确认号给服务端。服务端最终关闭。
socket其实不是一个协议,而是为了方便使用tcp或者udp而抽象出来的一个层次。是在应用层和传输层之间的一组接口。当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
五、示例
客户端websocket示例:
//1、申请一个Websocket对象,参数是需要连接服务端的地址,同 HTTP 协议开头一样,WebSocket 协议的 URL 使用 ws://开头,另外安全的 WebSocket 协议使用 wss://开头。
var ws = new WebSocket(“ws://echo.websocket.org”);
//WebSocket 对象一共支持四个消息 onopen, onmessage, onclose 和 onerror
ws.onopen = function(){ws.send(“Test!”); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log(“WebSocketClosed!”);};
ws.onerror = function(evt){console.log(“WebSocketError!”);};
当浏览器和服务端连接成功后,会触发onop嗯消息,
如果失败,会触发onerror消息。
当浏览器收到服务端发送过来的数据时会触发onmessage消息,
当浏览器收到服务端发送关闭连接请求时,会触发onclose消息。
所有的操作都是才送一步回调的方式触发。
六、后端代码
//开启WebSocket支持
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
/**
* Frozen
* 2020年3月30日21:22:55
*/
@ServerEndpoint("/redant")
@Component
public class FrozenTest {
/**
* 正常连接
* @throws Exception
*/
@OnOpen
public void onOpen(Session session) throws Exception {
sendMessage(session, "我还是从前哪个少年");
Thread.sleep(10000);//10秒后再发个
sendMessage(session, "嗨,HoYL,这是10秒之后的服务器推送数据!");
Thread.sleep(5000);//10秒后再发个
for (int i = 0; i < 100; i++) {
Random random = new Random();
sendMessage(session, String.valueOf(Math.random()*1000));
Thread.sleep(5000);//10秒后再发个
}
}
/**
* 实现服务器主动推送
*/
public void sendMessage(Session session, String message) throws Exception {
session.getBasicRemote().sendText(message);
}
}
七、前端测试代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>心跳测试</title>
<script type="text/javascript" src="frozenjs/websocket.js" ></script>
<script type="text/javascript" src="frozenjs/jquery-1.10.2.min.js" ></script>
</head>
<body>
<div>
<span id="suppose" style="color: red; font-weight: bolder;"></span>
</div>
</body>
<script>
var url = "ws://127.0.0.1:8088/rest/2020/redant";
var ws = new WebSocket(url);
ws.onclose = function (e) {
};
ws.onerror = function (e) {
};
ws.onopen = function () {
alert("与服务器连接成功!");
};
ws.onmessage = function (event) {
$("#suppose").text(event.data);
}
</script>
</html>
八、上图
1、服务端客户端握手成功
2、第一次服务器推送数据
3、第二次服务器推送数据
4、后续哪个漫长的等待