java websocket客户端断线重连(一文读懂 | WebSocket长连接通信过程与实现)java教程 / Java Web应用程序中的WebSocket通信...

wufei123 发布于 2024-06-04 阅读(15)

什么是 WebSocket ?WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较高。

另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。

而 WebSocket 解决了 HTTP 的这几个难题首先,当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通讯,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。

随着新标准的推进,WebSocket 已经比较成熟了,并且各个浏览器对 WebSocket 的支持情况比较好,有空可以看看。

---------相关视频推荐:学习地址:c/c++ linux服务器开发/后台架构师需要C/C++ Linux服务器架构师学习资料加裙994289133获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg

等),免费分享。

使用 WebSocket 的时候,前端使用是比较规范的,js 支持 ws 协议,感觉类似于一个轻度封装的 Socket 协议,只是以前需要自己维护 Socket 的连接,现在能够以比较标准的方法来进行客户端请求报文及实现

客户端请求报文:GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: example.com Origin: http://example.com Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy

与传统 HTTP 报文不同的地方:Upgrade: websocket Connection: Upgrade这两行表示发起的是 WebSocket 协议Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== Sec-WebSocket-Version: 13。

Sec-WebSocket-Key 是由浏览器随机生成的,提供基本的防护,防止恶意或者无意的连接Sec-WebSocket-Version 表示 WebSocket 的版本,最初 WebSocket 协议太多,不同厂商都有自己的协议版本,不过现在已经定下来了。

如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader,里面包含服务端支持的版本号创建 WebSocket 对象:var ws = new websocket("ws://127.0.0.1:8001");。

ws 表示使用 WebSocket 协议,后面接地址及端口完整的客户端代码: var ws; var box = document.getElementById(box); function startWS() {undefined ws = new WebSocket(ws://127.0.0.1:8001); ws.onopen = function (msg) {undefined console.log(WebSocket opened!); }; ws.onmessage = function (message) {undefined console.log(receive message: + message.data); box.insertAdjacentHTML(beforeend,

+ message.data +

); }; ws.onerror = function (error) {undefined console.log(Error: + error.name + error.number); }; ws.onclose = function () {undefined console.log(WebSocket closed!); }; } function sendMessage() {undefined console.log(Sending a message...); var text = document.getElementById(text); ws.send(text.value); } window.onbeforeunload = function () {undefined websocket.onclose = function () {}; // 首先关闭 WebSocket websocket.close() };

服务端响应报文及实现首先我们来看看服务端的响应报文HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat

我们一行行来解释1、首先,101 状态码表示服务器已经理解了客户端的请求,并将通过 Upgrade 消息头通知客户端采用不同的协议来完成这个请求;2、然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的

Sec-WebSocket-Key;3、最后,Sec-WebSocket-Protocol 则是表示最终使用的协议Sec-WebSocket-Accept 的计算方法:1、将 Sec-WebSocket-Key。

跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;2、通过 SHA1 计算出摘要,并转成 base64 字符串PS:Sec-WebSocket-Key / Sec-WebSocket-Accept。

的换算,只能带来基本的保障,但连接是否安全、数据是否安全、客户端 / 服务端是否合法的 ws 客户端、ws 服务端,其实并没有实际性的保证创建主线程,用于实现接受 WebSocket 建立请求:def create_socket(): # 启动 Socket 并监听连接 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.bind((127.0.0.1, 8001)) # 操作系统会在服务器 Socket 被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.listen(5) except Exception as e: logging.error(e) return else: logging.info(Server running...) # 等待访问 while True: conn, addr = sock.accept() # 此时会进入 waiting 状态 data = str(conn.recv(1024)) logging.debug(data) header_dict = {} header, _ = data.split(r\r\n\r\n, 1) for line in header.split(r\r\n)[1:]: key, val = line.split(: , 1) header_dict[key] = val if Sec-WebSocket-Key not in header_dict: logging.error(This socket is not websocket, client close.) conn.close() return magic_key = 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 sec_key = header_dict[Sec-WebSocket-Key] + magic_key key = base64.b64encode(hashlib.sha1(bytes(sec_key, encoding=utf-8)).digest()) key_str = str(key)[2:30] logging.debug(key_str) response = HTTP/1.1 101 Switching Protocols\r\n \ Connection: Upgrade\r\n \ Upgrade: websocket\r\n \ Sec-WebSocket-Accept: {0}\r\n \ WebSocket-Protocol: chat\r\n\r\n.format(key_str) conn.send(bytes(response, encoding=utf-8)) logging.debug(Send the handshake data) WebSocketThread(conn).start()。

进行通信解析 WebSocket 报文及实现Server 端接收到浏览器发来的报文需要进行解析浏览器包格式

1、FIN: 占 1 个 bit0:不是消息的最后一个分片1:是消息的最后一个分片2、RSV1, RSV2, RSV3:各占 1 个 bit一般情况下全为 0当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非。

0,且值的含义由扩展进行定义如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错3、Opcode: 4 个 bit%x0:表示一个延续帧当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片;。

%x1:表示这是一个文本帧(frame);%x2:表示这是一个二进制帧(frame);%x3-7:保留的操作代码,用于后续定义的非控制帧;%x8:表示连接断开;%x9:表示这是一个 ping 操作;%xA:表示这是一个 pong 操作;

%xB-F:保留的操作代码,用于后续定义的控制帧4、Mask: 1 个 bit表示是否要对数据载荷进行掩码异或操作0:否1:是5、Payload length: 7bit or 7 + 16bit or 7 + 64bit。

表示数据载荷的长度x 为 0~126:数据的长度为 x 字节;x 为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度;x 为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。

6、Masking-key: 0 or 4bytes当 Mask 为 1,则携带了 4 字节的 Masking-key;当 Mask 为 0,则没有 Masking-keyPS:掩码的作用并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)等问题。

7、Payload Data: 载荷数据解析 WebSocket 报文代码如下:def read_msg(data): logging.debug(data) msg_len = data[1] & 127 # 数据载荷的长度 if msg_len == 126: mask = data[4:8] # Mask 掩码 content = data[8:] # 消息内容 elif msg_len == 127: mask = data[10:14] content = data[14:] else: mask = data[2:6] content = data[6:] raw_str = # 解码后的内容 for i, d in enumerate(content): raw_str += chr(d ^ mask[i % 4]) return raw_str

服务端发送 WebSocket 报文返回时不携带掩码,所以 Mask 位为 0,再按载荷数据的大小写入长度,最后写入载荷数据struct 模块解析struct.pack(fmt, v1, v2, ...)。

按照给定的格式 (fmt),把数据封装成字符串 ( 实际上是类似于 c 结构体的字节流 )struct 中支持的格式如下表:

为了同 C 语言中的结构体交换数据,还要考虑有的 C 或 C++ 编译器使用了字节对齐,通常是以 4 个字节为单位的 32 位系统,故而 struct 根据本地机器字节顺序转换可以用格式中的第一个字符来改变对齐方式,定义如下:。

发送 WebSocket 报文代码如下:def write_msg(message): data = struct.pack(B, 129) # 写入第一个字节,10000001 # 写入包长度 msg_len = len(message) if msg_len <= 125: data += struct.pack(B, msg_len) elif msg_len <= (2 ** 16 - 1): data += struct.pack(!BH, 126, msg_len) elif msg_len <= (2 ** 64 - 1): data += struct.pack(!BQ, 127, msg_len) else: logging.error(Message is too long!) return data += bytes(message, encoding=utf-8) # 写入消息内容 logging.debug(data) return data

websocket 实现长连接原理 Websocket是一个 持久化的协议,相对于HTTP这种 非持久的协议来说 简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释 1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么 。

在HTTP1.0 中,这次HTTP请求就结束了 在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。

但是请记住 Request = Response , 在HTTP中永远是这样,也就是说一个request只能有一个response而且这个response也是被动的,不能主动发起WebSocket模式客户端与服务器请求响应模式如下图:。

WebSocket是类似Socket的TCP长连接通讯模式一旦WebSocket连接建立后,后续数据都以帧序列的形式传输在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。

在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显WebSocket有以下特点:是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。

而HTTP长连接基于HTTP,是传统的客户端对服务器发起请求的模式HTTP长连接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换HTTP header,信息交换效率很低Websocket协议通过第一个request建立了TCP连接之后,之后交换的数据都不需要发送 HTTP header就能交换数据,这显然和原有的HTTP协议有区别所以它需要对服务器和客户端都进行升级才能实现(主流浏览器都已支持HTML5)。

此外还有 multiplexing、不同的URL可以复用同一个WebSocket连接等功能这些都是HTTP长连接不能做到的总结没有其他能像 WebSocket 一样实现双向通信的技术了,迄今为止,大部分开发者还是使用

Ajax 轮询来实现,但这是个不太优雅的解决办法,WebSocket 虽然用的人不多,可能是因为协议刚出来的时候有安全性的问题以及兼容的浏览器比较少,但现在都有解决如果你有这些需求可以考虑使用 WebSocket:。

1 、多个用户之间进行交互;2、需要频繁地向服务端请求更新数据。比如弹幕、消息订阅、多玩家游戏、协同编辑、股票基金实时报价、视频会议、在线教育等需要高实时的场景。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻76391