一、【作用】 为了保持连接的可持续性和稳定性,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>