首页 > 解决方案 > 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"

标签: pythonwebsocketpermessagedeflate

解决方案


经过大量的调查,我发现我的问题是“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"

推荐阅读