定义
WebSocket(RFC 6455)是基于TCP的全双工通信协议,通过HTTP Upgrade握手(Upgrade: websocket、Sec-WebSocket-Key等)建立持久连接,之后双方可随时发送任意帧,无需请求-响应模式。相比HTTP轮询,WebSocket延迟低、头部开销小(仅2–14字节帧头),无连接建立开销,适合物联网实时监控、设备控制、即时通信、实时大屏等场景。
协议与帧格式
WebSocket握手后,数据以帧(Frame)形式传输,包含操作码(Opcode:文本/二进制/关闭/心跳)、掩码(客户端→服务端必掩码)、载荷长度。支持子协议(Subprotocol)协商,如 mqtt、wamp。Ping/Pong帧用于保活(Heartbeat),可检测连接存活。基于TCP,可叠加TLS实现WSS加密。
与MQTT、HTTP/2对比
MQTT面向消息发布订阅,有Broker、Topic、QoS等语义;WebSocket是传输层协议,无业务语义。HTTP/2支持多路复用与服务器推送,但仍是请求-响应模型。WebSocket适合需要双向实时流的场景(如实时监控、设备控制台),MQTT适合设备与云的消息通信;二者可结合,如MQTT over WebSocket。
达希物联云平台的控制台、实时监控大屏等Web应用,采用WebSocket实现设备状态、告警的实时推送,避免轮询带来的延迟与带宽浪费。设备侧可通过WebSocket与平台建立长连接,配合MQTT over WebSocket实现受限环境下的设备接入。达希物联的物联网卡为设备提供稳定网络,保障WebSocket连接不断线。
单条TCP上的帧并不等于一条业务消息
WebSocket的技术含义从握手结束之后才开始。HTTP Upgrade只把这条TCP从一问一答改成帧通道:客户端给出Sec-WebSocket-Key,服务端回Sec-WebSocket-Accept,此后字节不再按HTTP头解析。帧头用操作码标明文本、二进制、延续、关闭、Ping或Pong。一条业务消息可以由多帧拼成,只有第一帧携带真正的操作码,后面的帧标为延续。接收方若按“一次读事件等于一条指令”切包,会把半条JSON当成完整报文,或把粘在一起的两帧拆开。
掩码不是加密。规范要求客户端发往服务端的帧必须掩码,服务端发往客户端的帧不得掩码,以免有缺陷的中间缓存把上行数据当成可缓存的HTTP正文。浏览器会自行掩码;模组或网关上的自写协议栈漏了掩码位,对端会以协议错误关闭连接。服务端若错误地掩码下发,浏览器会拒绝这些帧。机密性靠外层TLS,也就是接口里的wss;帧格式与明文的ws相同,只是套在TLS记录里面。
这条通道也不带订阅、离线队列或服务质量等级。子协议只是握手头Sec-WebSocket-Protocol里声明的应用名。要在同一条连接上跑MQTT,服务端只能从客户端给出的名字里选一个,不能另起一个客户端没提过的名字;选不中就应让握手失败,而不是降成裸通道再碰运气。因此它解决的是浏览器或网关与服务器谁都可以先开口的传输问题。主题、应答和补传要么自己定义,要么交给已经谈妥的子协议。
双向开口值得占用一条长连接时
它适合留下,是因为页面或网关既要收服务器主动推来的状态,也要随时把操作送回去,并且中间路径允许Upgrade、愿意长期维持这条TCP。监控墙、远程调试台、边看边下发的维护会话属于这一类。设备若只是隔几分钟上报一次、不需要服务器中途插入指令,短请求或受限节点上的CoAP更合适,不必为每次上电支付TCP握手、TLS握手和升级往返。
连接要活过路径上最短的空闲回收。负载均衡、企业代理和蜂窝地址转换都会在一段时间没有字节之后拆掉映射,本机套接字却仍显示已连接,直到下一次写失败。Ping帧应短于这条路径实测到的空闲上限,并要求对端回Pong;应用层打印“已连接”不能代替保活。间隔若卡在代理的切断时间上,会出现时通时断。蜂窝侧的重连还要错开,避免同一基站下的模组在网络恢复后同时发起升级握手。
它也替代不了带队列语义的消息协议,更不适合在一条连接里混许多互不相干的大块传输。WebSocket骑在单条TCP上,一个固件块占住发送窗口时,后面已经生成的告警帧也得排队,这是队头阻塞。业务若是多条独立流,又跑在容易丢包的移动网络上,按流确认的QUIC往往更合适。把MQTT放进WebSocket,只在必须穿过只放行HTTPS的代理时有意义;模组能直接连经纪节点的端口时,多这一层升级只是多一个握手失败点。
套接字还在、推送已经停了
TCP确认只说明字节进了内核缓冲区。服务端线程被慢消费者堵住时,设备仍会收到确认并继续采集,平台侧却看不到数据。send返回成功也不能当成指令已经执行,应用层需要自己的序号或应答。关闭应发送关闭帧并带上原因:正常结束、端点离开、报文太大或策略拒绝,含义不同。直接拆掉TCP会让对端停在半开,重连时旧连接占用的代理槽位可能还没释放。
帧级别的实现错误更隐蔽。文本帧必须是合法UTF-8,把传感器二进制塞进文本帧,会在校验处被断开,现象像随机掉线。控制帧不能拆分;Ping带出的载荷必须原样出现在Pong里,中间设备改写这段载荷,保活会被判失败。单帧或整条消息超过服务端上限时,对端用关闭帧拒绝,客户端若无限重试,会把握手打满。逐消息压缩能减小JSON,但网关CPU很紧时会把保活推迟到超时之后。
鉴权若只放在升级请求的Cookie或首包令牌里,握手通过后这条连接就继承了当时的身份。令牌过期不会自动拆连接,除非应用层再查一次或主动关闭。共享工位上的浏览器、被转发出去的调试代理,都可以把已经升级的连接继续用下去。设备侧重连时也不能复用上一次的Sec-WebSocket-Key,每个新握手都要新的随机值,重放旧的升级请求会被按规范拒绝。