Websocket Permessage-deflate не происходит в направлении сервера → клиента
Я написал свою собственную реализацию веб-сокета на Python, чтобы научиться их внутреннему устройству. Я собирался отправлять большие повторяющиеся объекты JSON через веб-узел, поэтому я пытаюсь внедрить
Запрос
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. Я также попытался отправить данные в двоичном формате через веб-узел и получить на другом конце двоичный объект. Я уже некоторое время рыскал по Интернету и не слышал об этом. Думаю, я сжимаю данные не на том шаге. Ниже представлена функция, которую я использую для отправки данных. До сих пор я загружал сжатое сообщение в
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"
1 ответ
После долгих поисков я обнаружил, что моей проблемой был "RTFM". В третьем абзаце (а?) В Manua л на perMessage сжатии он говорит
Клиент WebSocket может предлагать несколько PMCE во время
рукопожатия открытия WebSocket. Одноранговый сервер WebSocket, получивший эти предложения, может выбрать и принять предпочтительное одно или отклонить все из них. PMCE используют
бит RSV1 заголовка кадра WebSocket, чтобы указать,
сжато сообщение или нет, так что конечная точка может решить не
сжимать сообщения с несжимаемым содержимым.
Я не знал, что делают биты 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"