WebSocket(贰) 解析数据帧

简介: 知道了怎么握手只是让客户端和服务器建立连接而已,WebSocket真正麻烦的地方是在数据的传输上!为了环保,它使用了特定格式的数据帧,这个数据帧需要自己去解析(当然也有别人编写好的库可以用)。


知道了怎么握手只是让客户端和服务器建立连接而已,WebSocket真正麻烦的地方是在数据的传输上!为了环保,它使用了特定格式的数据帧,这个数据帧需要自己去解析(当然也有别人编写好的库可以用)。虽然官方文档描述的很详细,但是看起来还是蛋疼。
  当客户端向服务器发送一个数据时服务器收到一个数据帧,比如下面的程序:

//客户端程序
var ws=new WebSocket("ws://127.0.0.1:8000");
ws.onopen=function(e){
  ws.send("次碳酸钴"); //发送数据
};
===============================================================

//服务器程序
var crypto=require('crypto');
var WS='258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

require('net').createServer(function(o){
  var key;
  o.on('data',function(e){
    if(!key){ //握手
      key=e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
      key=crypto.createHash('sha1').update(key+WS).digest('base64');
      o.write('HTTP/1.1 101 Switching Protocols\r\n');
      o.write('Upgrade: websocket\r\n');
      o.write('Connection: Upgrade\r\n');
      o.write('Sec-WebSocket-Accept: '+key+'\r\n');
      o.write('\r\n');
    }else onmessage(e); //接收并交给处理函数
  });
}).listen(8000);

这里是直接把接收到的数据输出了,得到这样一个东西


这就是一个完整的数据帧,直接的16进制数据我们当然无法直接阅读,需要按照数据帧的格式把它里面的数据取出来才行。对于这个数据帧,官方文档提供了一个结构图

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

光拿出这个实在很难看懂,顶部数字用十进制而不是八进制太让人蛋疼了。当然官方文档在后面的描述中也有详细介绍,看完后再回头来看图表才能看明白。其实WebSocket目前还不太完善,很多实验性的东西,所以完全按照官方文档来理解是蛋疼的。这里就说我自己的理解。
  现在再看左上角上面的图标,左上角的四个小列,也就是4位,第一位是FIN,后面三位是RSV1到3。官方文档上说RSV是预留的空间,正常为0,这就意味着,正常情况下他们可以当做0填充,那么前4位只有第一位的FIN需要设置,FIN表示帧结束,由于这篇中它不重要就不特别介绍了。接着后面的四位是储存opcode的值,这个opcode是标识数据类型的。这样数据的第一个字节我们就能理解它的含义了,看上面16进制的数据的第一个字节81换成二进制是1000001,第一个1是FIN的值,最后一个1是opcode的值。
  接着是第二个字节的数据,它由1位的MASK和7位的PayloadLen组成,MASK标识这个数据帧的数据是否使用掩码,PayloadLen表示数据部分的长度。但是PayloadLen只有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然无法描述较大的数据,因此规定当数据长度小于或等于125时候它才作为数据长度的描述,如果这个值为126,则时候后面的两个字节来储存储存数据长度,如果为127则用后面八个字节来储存数据长度。所以上面的图片第一行的最右侧那块和第二行看起来有些颓然。从我们的示例数据来看,第二个字节的8C中80是最高位为1,这意味着MASK为1,后面的C表示这个数据部分有12个字节。
  再接着是上面图表中的MaskingKey,它占四个字节,储存掩码的实体部分。但是只有在前面的MASK被设置为1时候才存在这个数据,否则不使用掩码也就没有这个数据了。看我们的示例数据,由于前面的MASK为1,所以3到6字节的“79 77 3d 41”是数据的掩码实体。
  最后是数据部分,如果掩码存在,那么所有数据都需要与掩码做一次异或运算,四个字节的掩码与所有数据字节轮流发生性关系。如果不存在掩码,那么后面的数据就可以直接使用。
  这样数据帧就解析完了。下面是我写的数据帧解析的程序,请不要吐槽代码没优化


function decodeDataFrame(e){
  var i=0,j,s,frame={
    //解析前两个字节的基本数据
    FIN:e[i]>>7,Opcode:e[i++]&15,Mask:e[i]>>7,
    PayloadLength:e[i++]&0x7F
  };
  //处理特殊长度126和127
  if(frame.PayloadLength==126)
    frame.PayloadLength=(e[i++]<<8)+e[i++];
  if(frame.PayloadLength==127)
    i+=4, //长度一般用四字节的整型,前四个字节通常为长整形留空的
    frame.PayloadLength=(e[i++]<<24)+(e[i++]<<16)+(e[i++]<<8)+e[i++];
  //判断是否使用掩码
  if(frame.Mask){
    //获取掩码实体
    frame.MaskingKey=[e[i++],e[i++],e[i++],e[i++]];
    //对数据和掩码做异或运算
    for(j=0,s=[];j 
既然有了解析程序,那么我们就可以把上面实例服务器端的onmessage方法修改一下

function onmessage(e){
  e=decodeDataFrame(e); //解析数据帧
  console.log(e); //把数据帧输出到控制台
};



这样服务器接收客户端穿过了的数据就没问题了。嘛,这篇文章就只说接收,至于从服务器发送到客户的情况会有更复杂的情况出现,咱下一篇再说。

目录
相关文章
|
运维 负载均衡 安全
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
363 0
|
8月前
|
JSON 监控 物联网
WebSocket 调试全攻略:核心解析、工具选择与对比!
WebSocket 是一种全双工、实时交互的网络通信协议,适用于即时通信、实时数据流、多人协作、IoT 等场景。调试 WebSocket 时,工具应具备握手管理、实时消息收发、自定义 Header、消息大小告警、分组管理、多连接支持和断线重现等功能。主流调试工具如 Postman、ApiPost 和 ApiFox 各有优劣:Postman 界面友好适合基础调试;ApiPost 支持高级功能如消息分组和自动重连;ApiFox 则强化了多连接支持。选择工具时需根据具体需求和团队熟悉度决定。
|
8月前
|
JSON 自然语言处理 前端开发
WebSocket调试工具深度对比:Postman与Apipost功能实测解析
本文深入对比了Postman与Apipost两款WebSocket调试工具。作为实时通讯系统工程师,作者在开发智能客服系统时遇到了传统工具调试复杂、文档管理不便的问题。通过引入Apipost的智能连接池、消息分组管理和自动化文档生成等功能,实现了多环境自动切换、消息分类和接口文档自动生成,极大提升了调试效率和团队协作效果。最终,使用Apipost使接口调试时间减少40%,文档维护成本降低70%,跨团队沟通效率提升50%。
|
8月前
|
JSON 监控 物联网
#WebSocket 调试全攻略:Postman、Apipost和Apifox核心解析、工具选择与对比!
WebSocket 是一种现代化的全双工通信协议,允许客户端和服务端通过持久连接实时双向传输数据。它适用于即时通讯、实时通知、金融行情、在线协作、物联网等场景。调试 WebSocket 时,工具应具备握手管理、实时消息收发、自定义 Header、消息大小监控、分组管理、多连接支持等功能。
726 1
|
9月前
|
JSON 前端开发 安全
WebSocket调试工具深度对比:Postman与Apipost功能实测解析
如果你在寻找既能搞定WebSocket调试,又能完美管理文档的工具,不妨试试Apipos!
299 1
|
监控 网络协议 API
.NET WebSocket 技术深入解析,你学会了吗?
【9月更文挑战第4天】WebSocket 作为一种全双工协议,凭借低延迟和高性能特点,成为实时应用的首选技术。.NET 框架提供了强大的 WebSocket 支持,使实时通信变得简单。本文介绍 WebSocket 的基本概念、.NET 中的使用方法及编程模型,并探讨其在实时聊天、监控、在线游戏和协同编辑等场景的应用,同时分享最佳实践,帮助开发者构建高效实时应用。
389 12
|
运维 负载均衡 前端开发
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
【7月更文挑战第16天】Python Web开发中,前后端分离常见于实时通信场景,WebSocket作为全双工协议,常用于此类应用。选型时考虑性能、功能、易用性、社区支持和成本。Flask-SocketIO是实现WebSocket的一个选项,它简化了与Flask的集成。案例展示了如何用Flask-SocketIO创建一个实时聊天室:后端处理消息广播,前端通过Socket.IO库连接并显示消息。此实现策略演示了在Python中实现实时通信的基本步骤。
298 0
|
前端开发 网络协议 JavaScript
在Spring Boot中实现基于WebSocket的实时通信
在Spring Boot中实现基于WebSocket的实时通信
|
6月前
|
Java
SpringBoot快速搭建WebSocket服务端和客户端
由于工作需要,研究了SpringBoot搭建WebSocket双向通信的过程,其他的教程看了许多,感觉讲得太复杂,很容易弄乱,这里我只展示快速搭建过程。
2005 1
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
3079 1

推荐镜像

更多
  • DNS
  • 下一篇
    oss云网关配置