python - Websocket Permessage-deflate 未发生在服务器-> 客户端方向
问题描述
我已经在 python 中编写了自己的 websocket 实现,以自学它们的内部工作原理。我打算通过 websocket 发送大量重复的 JSON 对象,所以我正在尝试实现permessage-deflate
. 压缩在client->server
方向上起作用,但不是在 server -> client
方向上
这是标头交换:
要求
Host: awebsite.com:port
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Upgrade: websocket
Origin: http://awebsite.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Sec-WebSocket-Key: JItmF32mfGXXKYyhcEoW/A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
回复
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Accept: zYQKJ6gvwlTU/j2xw1Kf0BErg9c=
当我这样做时,我会按预期从客户端获取压缩数据并按预期膨胀。
当我发送未压缩的消息时,我会在客户端得到正常响应,即我发送“你好”并得到“你好”
当我尝试使用这个简单的 python 函数缩小我的消息时:
def deflate(self,data,B64_encode=False):
data=zlib.compress(data)
if B64_encode:
return base64.b64encode(data[2:-4])
else:
return data[2:-4]
我收到一条关于字符不是 utf-8 的错误消息,当我对压缩消息进行 base64 编码时,我只得到了 base64 编码的字符串。我还尝试通过 websocket 将数据作为二进制发送,并在另一端得到一个 blob。我已经在互联网上搜索了一段时间,还没有听说过这种情况。我的猜测是我在错误的步骤压缩数据。下面是我用来发送数据的函数。到目前为止,我一直在将压缩消息输入到send()
函数中,因为从我读过的内容来看,permessage 压缩发生在消息级别,并且所有其他数据保持未压缩。
def send(self, string, TYPE="TEXT"):
import struct
conn = self.conn
datatypes = {
"TEXT": 0x01,
"BINARY": 0x02,
"CLOSE": 0X08,
"PING": 0x09,
"PONG": 0x0A}
b1 = 0x80
b2 = 0
message = ""
if TYPE == "TEXT":
if type(string) == unicode:
b1 |= datatypes["TEXT"]
payload = string.encode("UTF8")
elif type(string) == str:
b1 |= datatypes["TEXT"]
payload = string
message += chr(b1)
else:
b1 |= datatypes[TYPE]
payload = string
message += chr(b1)
length = len(payload)
if length < 126:
b2 |= length
message += chr(b2)
elif length < (2 ** 16) - 1:
b2 |= 126
message += chr(b2)
l = struct.pack(">H", length)
message += l
else:
l = struct.pack(">Q", length)
b2 |= 127
message += chr(b2)
message += l
message += payload
try:
conn.send(str(message))
except socket.error:
traceback.print_exc()
conn.close()
if TYPE == "CLOSE":
self.Die = True
conn.shutdown(2)
conn.close()
print self.myid,"Closed"
解决方案
经过大量的调查,我发现我的问题是“RTFM”的情况。在 (a?)的perMessage Compression 手册的第三段中,它说
WebSocket 客户端可以在 WebSocket打开握手期间提供多个 PMCE 。收到这些提议的对等 WebSocket 服务器可以选择并接受首选的一个或拒绝所有这些提议。PMCE 使用
WebSocket 帧头的 RSV1 位来指示
消息是否被压缩,以便端点可以选择不
压缩内容不可压缩的消息。
当我第一次设置它时,我不知道 rsv 位做了什么,并且默认将它们设置为 0。send()
我的代码现在允许在我的程序的函数中设置压缩。它很好地将我的消息从 30200 字节缩小到 149 字节。
我修改后的代码现在看起来像这样:
def deflate2(self,data):
data=zlib.compress(data)
data=data[2:-4]
return data
def send(self, string, TYPE="TEXT",deflate=False):
import struct
if (deflate):
string=self.deflate(string)
conn = self.conn
datatypes = {
"TEXT": 0x01,
"BINARY": 0x02,
"CLOSE": 0X08,
"PING": 0x09,
"PONG": 0x0A}
b1 = 0x80 #0b100000000
if(deflate): b1=0xC0 #0b110000000 sets RSV1 to 1 for compression
b2 = 0
message = ""
if TYPE == "TEXT":
if type(string) == unicode:
b1 |= datatypes["TEXT"]
payload = string.encode("UTF8")
elif type(string) == str:
b1 |= datatypes["TEXT"]
payload = string
message += chr(b1)
else:
b1 |= datatypes[TYPE]
payload = string
message += chr(b1)
length = len(payload)
if length < 126:
b2 |= length
message += chr(b2)
elif length < (2 ** 16) - 1:
b2 |= 126
message += chr(b2)
l = struct.pack(">H", length)
message += l
else:
l = struct.pack(">Q", length)
b2 |= 127
message += chr(b2)
message += l
message += payload
try:
if self.debug:
for x in message: print("S>: ",x,hex(ord(x)),ord(x))
conn.send(str(message))
except socket.error:
traceback.print_exc()
conn.close()
if TYPE == "CLOSE":
self.Die = True
conn.shutdown(2)
conn.close()
print self.myid,"Closed"
推荐阅读
- android - BluetoothGattCallback - onConnectionStateChange 返回状态 40
- python - 使用带有数据框字典的查找表
- matlab - 加载带有索引名称的文件
- javascript - 在特定路线上禁用 v-navigation-drawer,Vue-js
- .net-core - 如何在公司代理/防火墙后面使用 .NET Core CLI?
- ios - 如何为标签和按钮水平设置自动布局?
- apache-spark - 从 Spark Dataframe 中获取表名
- javascript - 正则表达式:防止字符串以特定的特殊字符结尾
- flutter - 使用用户输入模糊图像的一部分 [Flutter]
- android - 如何在我的应用程序中显示隐私政策