Delphi 的Websocket Server 控件实现(一、WebSocket 原理)(含源码)

目录

一、WebSocket 原理

1、握手handshaking)

2、数据通信

3、通讯示例

二、WebSocket 通信需要注意点说明

1、关于帧段(碎片)规则说明

2、通信示例

待续...


一、WebSocket 原理

关于控件的源码下载见《Delphi 的Websocket Server 控件实现(四、WebSocket Demo程序使用说明)

WebSocket 标准使用 UTF8编码通信,切记!

关于WebSocket 的原理,主要是RFC 6455标准,该标准的原文参见官方:RFC 6455(2011.11),如果大家不好下载,可以直接下载这个链接:WebSoket_协议(rfc6455).pdf(71页)

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。关于WebSocket浏览器端javascript的实现使用请参见《菜鸟教程》的HTML5 WebSocket,说的非常清楚。

WebSocket 是独立的、创建在 TCP 上的协议。

Websocket 通过HTTP/1.1 协议的101状态码进行握手。

为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

握手完成之后,客户端和服务器就建立了持久连接,可以进行通信,除非任何一端主动断开。在通信过程中,客户端发给服务器端的数据是需要掩码计算的,而服务器端发给客户端的不需要。

1、握手handshaking)

客户端主动发起,客户端会给服务器端发送类似如下文本数据:

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:3002
Origin: http://127.0.0.1:8020
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: Zi5YEXnU4yRo6R4hg1Wsdw==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.138 Safari/537.36

      注意客户端发送的是 GET / HTTP/1.1 打头命令,必须告诉服务器端Upgrade为websocket,Connection为Upgrade,这样服务器端就知道客户端需要进行websocket通信,此时服务器端需要取得Sec-WebSocket-Key中的钥匙串Zi5YEXnU4yRo6R4hg1Wsdw==,通过和“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接再SHA-1签名后Base64编码返回给客户端,客户端验证通过就完成了握手流程。

Zi5YEXnU4yRo6R4hg1Wsdw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11,对这个字符串进行SHA-1签名再Base64编码,注意连接两个字符串的时候中间不能有空格。结果为:hUfGucluAOxLIDaafqTtjEZFjMs=,见下面。

服务器返回给客户端的数据如下:

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: hUfGucluAOxLIDaafqTtjEZFjMs=

服务器返回101结果,同时继续返回Connection和Upgrade字段,并且返回经过签名的结果:hUfGucluAOxLIDaafqTtjEZFjMs=

2、数据通信

在数据通信过程中,使用的是帧概念,帧数据有结构规范。一个完整的数据帧,可以通过几个小帧段组成,基本的帧结构如下:

Delphi 的Websocket Server 控件实现(一、WebSocket 原理)(含源码)_第1张图片

FIN:1bit  表示是不是最后一个帧段,1表示是,否则表示不是。

RSV1、RSV2、RSV3:1 bit each  暂时不需要关注,默认是0,都是一位。

Opcode: 4 bits 定义实际帧数据的类型:

           0 : 表示是一个连续的Frame
           1 : 表示是一条文本消息
           2 : 表示是流消息
           3-7 : 保留
           8 : 表示一个连接关闭
           9 : 一个ping
           A : 一个pong
           B-F : 保留  

Mask:1 bit  表示数据是否需要进行反掩码接开,1表示需要,0表示不需要

Payload length:7 bits, 7+16 bits, or 7+64 bits 这个理解上需要转换下。如果此值介于0和125之间,则为消息的长度。如果是126,则以下2个字节(16位无符号整数)是长度。如果是127,则以下8个字节(64位无符号整数)是长度。

Masking-key: 0 or 4 bytes  从客户端发送到服务器端的所有数据都包含这4个字节,此时上面的Mask位设置也必须为1,服务器端需要通过这4个字节对后续数据进行反掩码计算。如果Mask位为0,则不包含这4个字节,表示数据无需进行反掩码运算,服务器端到客户端的数据都是这样的。

Payload data:实际的数据字节流。

3、通讯示例

假如客户端需要给服务器端发送4个字母:SZHN,那么服务器端实际收到的数据为:(129, 132, 91, 168, 122, 85, 8, 242, 50, 27)

转换为HEX就是($81,$84,$5B,$A8,$7A,$55,$08,$F2,$32,$1B)根据协议解释如下:

129($81) = 1 0 0 0 0 0 0 1     前面的第一个1表示的是FIN,这是最后一帧数据,最后面的4bit也是1,表示这是一个文本消息。

132($84)= 1 0 0 0 0 1 0 0     前面的1表示Mask位,这说明随后会有4个字节的Masking-key,需要进行反掩码计算,后面的000100就是4,表示的数据长度为4,因为我们发送的就是SZHN这4个字符。

91, 168,122,85  表示的是Masking-key,需要用这4个字节循环对后续的数据进行xor反掩码计算。

8,242, 50, 27 表示数据,进行反掩码后得到的数据如下:

91 xor 8 = 83 (S)   168 xor 242 = 90 (Z)  122 xor 50 = 72 (H)   85 xor 27 = 78 (N),接出来的结果正好是SZHN。

二、WebSocket 通信需要注意点说明

1、关于帧段(碎片)规则说明

对于一帧数据(Frame),可以分成几个帧段(碎片fragmentation)发送,这样的话,规则定义如下:

  • 非分段消息由带有FIN(FIN必须设置为1)的单个框架组成和0以外的操作码;
  • 分段消息由一个带有FIN位为0并输入一个非0的操作码,后跟零个或多个帧FIN位清除,操作码设置为0,终止于一个FIN位设置为1和0操作码的单帧

举例如下:对于作为三个片段发送的文本消息,第一个片段的操作码为0x1,FIN位为0;第二个片段的操作码为0x0,FIN位为0,
    第三个片段有一个0x0操作码和一个FIN位为1,说明整个数据已经准备好了。

2、通信示例

编码 类型 示例
1 单一非掩码帧 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello")
2 单一掩码帧 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains "Hello")
3 帧段(碎片)非掩码帧

0x01 0x03 0x48 0x65 0x6c (contains "Hel")

0x80 0x02 0x6c 0x6f (contains "lo")

4 非掩码ping请求和掩码ping返回

0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains a body of "Hello",内容随意)

0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains a body of "Hello", 必须匹配请求)

5 一帧中包含256个非掩码二进制数据 0x82 0x7E 0x0100 [256 bytes of binary data]
6 一帧包含64K非掩码二进制数据 0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]

继续....

你可能感兴趣的:(Delphi,delphi,websocket,client,server,html5,tcpip)