WebSocket断开连接
WebSocket是很民主的,啥都要协商!建立连接时需要握手协议,连断开连接都需要双方共同完成!其实断开连接直接断开TCP连接就可以了,但是这有点暴力。文明点的方法是发个请求,让对方自己断开。客户端要主动断开就必须向服务器发送8这个操作码。
首先是服务器主导断开的情况,最简单的方法是直接把TCP连接断开,这里就不演示了。由于这对客户端来说是个意外断开,WebSocket对象采取应急措施也触发close事件。咱是文明人,当然要做点有绅士风度的事情。于是咱不从服务器断开连接,而是向客户端发送个请求断开的操作码来请求客户端自己断开。
其实就是个操作码为8的帧。但要注意的是数据部分比较特殊。当然如果嫌麻烦可以不传,不过要是不传就和前面的霸王硬上弓一样无节操了。数据部分的前两个字节是状态码,之后的部分是关闭连接原因的文本描述,这些东西可以传到客户端。
(encodeDataFrame与decodeDataFrame函数见生成数据帧和解析数据帧)
1 //客户端程序 2 var ws=new WebSocket("ws://127.0.0.1:8000"); 3 ws.onclose=function(e){ 4 console.log(e); 5 ws.close(); //关闭TCP连接 6 };
========================================================
1 //服务器程序 2 var crypto=require('crypto'); 3 var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 4 require('net').createServer(function(o){ 5 var key; 6 o.on('data',function(e){ 7 if(!key){ 8 //握手 9 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; 10 key=crypto.createHash('sha1').update(key+WS).digest('base64'); 11 o.write('HTTP/1.1 101 Switching Protocols\r\n'); 12 o.write('Upgrade: websocket\r\n'); 13 o.write('Connection: Upgrade\r\n'); 14 o.write('Sec-WebSocket-Accept: '+key+'\r\n'); 15 o.write('\r\n'); 16 //构造断连请求的数据部分,前面留两字节存放状态码 17 var buf=new Buffer('\0\0孩子,地球太危险了,快回火星去吧!'); 18 buf.writeUInt16BE(1000,0); //在头两个字节写入一个状态码 19 //发送断连请求 20 o.write(encodeDataFrame({FIN:1,Opcode:8,PayloadData:buf})); 21 }; 22 }); 23 }).listen(8000);
客户端会在onclose的参数中接收到一个这样的东西,状态码和结束原因描述分别在code和reason两个参数中。规范文档中规定了很多状态码的含义,不过这个目前不是强制性的,我就不列举了。见RFC6455#section-7.4。客户端在收到服务器的这个断连请求后应该调用close方法来关闭,否则连接会先入停滞状态等待客户端响应。
服务器主导断开的情况就是这样。下面是客户端主导断开的情况。客户端先要调用close方法,这个操作会发送一个断连请求到服务器上,服务器收到这个请求后把TCP连接断开即可。但是服务器程序是自己写的,这个请求也需要自己解析。
1 //客户端程序 2 var ws=new WebSocket("ws://127.0.0.1:8000"); 3 ws.onopen=function(){ 4 ws.close(); //发起断连请求 5 }; 6 ws.onclose=function(e){ 7 console.log(e); 8 };
1 //服务端程序序 2 var crypto=require('crypto'); 3 var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 4 require('net').createServer(function(o){ 5 var key; 6 o.on('data',function(e){ 7 if(!key){ 8 //握手 9 key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; 10 key=crypto.createHash('sha1').update(key+WS).digest('base64'); 11 o.write('HTTP/1.1 101 Switching Protocols\r\n'); 12 o.write('Upgrade: websocket\r\n'); 13 o.write('Connection: Upgrade\r\n'); 14 o.write('Sec-WebSocket-Accept: '+key+'\r\n'); 15 o.write('\r\n'); 16 }else{ 17 var frame=decodeDataFrame(e); 18 console.log(frame); 19 if(frame.Opcode==8){ 20 //这里也可以发送个结束包来给客户端的onclose中带参数 21 //var buf=new Buffer('\0\0孩子,地球太危险了,快回火星去吧!'); 22 //buf.writeUInt16BE(1000,0); 23 //o.write(encodeDataFrame({FIN:1,Opcode:8,PayloadData:buf})); 24 o.end(); //断开连接 25 }; 26 }; 27 }); 28 }).listen(8000);
总之,客户端直接调用close方法并不会关闭连接,而是发送请求到服务器请求对方。服务器接收请求后可以断开连接。这会触发客户端的close事件。当然,在断开之前也可以发送个同样的断连请求,并包含状态码和原因描述。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义