通过Python socket/WebSocket Client发送/接收WebSocket消息

yak*_*yak 6 python sockets decode protocols websocket

我写了一个简单的WebSocket客户端.我使用了我在SO上找到的代码,这里:我如何在服务器端发送和接收WebSocket消息?.

我正在使用Python 2.7,我的服务器echo.websocket.org80TCP端口上.基本上,我认为接收消息时遇到问题.(或者也许发送错了?)

至少我确信握手没问题,因为我收到了很好的握手响应:

HTTP/1.1 101 Web Socket Protocol Handshake
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Headers: x-websocket-extensions
Access-Control-Allow-Headers: x-websocket-version
Access-Control-Allow-Headers: x-websocket-protocol
Access-Control-Allow-Origin: http://example.com
Connection: Upgrade
Date: Tue, 02 May 2017 21:54:31 GMT
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Kaazing Gateway
Upgrade: websocket
Run Code Online (Sandbox Code Playgroud)

我的代码:

#!/usr/bin/env python
import socket

def encode_text_msg_websocket(data):
    bytesFormatted = []
    bytesFormatted.append(129)

    bytesRaw = data.encode()
    bytesLength = len(bytesRaw)

    if bytesLength <= 125:
        bytesFormatted.append(bytesLength)
    elif 126 <= bytesLength <= 65535:
        bytesFormatted.append(126)
        bytesFormatted.append((bytesLength >> 8) & 255)
        bytesFormatted.append(bytesLength & 255)
    else:
        bytesFormatted.append(127)
        bytesFormatted.append((bytesLength >> 56) & 255)
        bytesFormatted.append((bytesLength >> 48) & 255)
        bytesFormatted.append((bytesLength >> 40) & 255)
        bytesFormatted.append((bytesLength >> 32) & 255)
        bytesFormatted.append((bytesLength >> 24) & 255)
        bytesFormatted.append((bytesLength >> 16) & 255)
        bytesFormatted.append((bytesLength >> 8) & 255)
        bytesFormatted.append(bytesLength & 255)

    bytesFormatted = bytes(bytesFormatted)
    bytesFormatted = bytesFormatted + bytesRaw
    return bytesFormatted


def dencode_text_msg_websocket(stringStreamIn):
    byteArray = [ord(character) for character in stringStreamIn]
    datalength = byteArray[1] & 127
    indexFirstMask = 2
    if datalength == 126:
        indexFirstMask = 4
    elif datalength == 127:
        indexFirstMask = 10
    masks = [m for m in byteArray[indexFirstMask: indexFirstMask + 4]]
    indexFirstDataByte = indexFirstMask + 4
    decodedChars = []
    i = indexFirstDataByte
    j = 0
    while i < len(byteArray):
        decodedChars.append(chr(byteArray[i] ^ masks[j % 4]))
        i += 1
        j += 1
    return ''.join(decodedChars)

# connect 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((socket.gethostbyname('echo.websocket.org'), 80))

# handshake
handshake = 'GET / HTTP/1.1\r\nHost: echo.websocket.org\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: gfhjgfhjfj\r\nOrigin: http://example.com\r\nSec-WebSocket-Protocol: echo\r\n' \
        'Sec-WebSocket-Version: 13\r\n\r\n'
sock.send(handshake)
print sock.recv(1024)

# send test msg
msg = encode_text_msg_websocket('hello world!')
sock.sendall(msg)

# receive it back
response = dencode_text_msg_websocket(sock.recv(1024))
print '--%s--' % response

sock.close()
Run Code Online (Sandbox Code Playgroud)

这有什么不对?握手后它变得复杂了.

dencode_text_msg_websocket方法返回一个空字符串,但它应该返回我发送给服务器的相同字符串,即hello world!.

我不想使用库(我知道如何使用它们).问题是关于在没有库的情况下实现同样的事情,仅使用套接字.

我只想发送消息echo.websocket.org server并收到回复,就是这样.我不想修改标题,只需像这个服务器使用的那样构建标题.我检查了它们应该如何使用Wireshark,并尝试使用Python构建相同的数据包.

对于下面的测试,我使用了我的浏览器:

从服务器到客户端的未屏蔽数据:

在此输入图像描述

从客户端到服务器的屏蔽数据:

在此输入图像描述

gus*_*ong 1

根据https://www.rfc-editor.org/rfc/rfc6455#section-5.1

\n

您应该屏蔽客户端框架。(并且服务器帧根本没有被屏蔽。)

\n
    \n
  • 客户端必须屏蔽它发送到服务器的所有帧(更多详细信息请参见第 5.3 节)。(请注意,无论 WebSocket 协议是否在 TLS 上运行,都会进行屏蔽。)服务器必须在收到未屏蔽的帧时关闭连接。在这种情况下,服务器可以发送一个状态码为 1002(协议错误)的 Close 帧,如第 7.4.1 节中所定义。 服务器不得屏蔽发送给客户端的任何帧。\n 如果客户端检测到屏蔽\nframe\xe3\x80\x82,则必须关闭连接
  • \n
\n
\n

这是一个工作版本:

\n
import os\nimport array\nimport six\nimport socket\nimport struct\n\nOPCODE_TEXT = 0x1\n\ntry:\n    # If wsaccel is available we use compiled routines to mask data.\n    from wsaccel.xormask import XorMaskerSimple\n    \n    def _mask(_m, _d):\n        return XorMaskerSimple(_m).process(_d)\n\nexcept ImportError:\n    # wsaccel is not available, we rely on python implementations.\n    def _mask(_m, _d):\n        for i in range(len(_d)):\n            _d[i] ^= _m[i % 4]\n\n        if six.PY3:\n            return _d.tobytes()\n        else:\n            return _d.tostring()\n\n\ndef get_masked(data):\n    mask_key = os.urandom(4)\n    if data is None:\n        data = ""\n\n    bin_mask_key = mask_key\n    if isinstance(mask_key, six.text_type):\n        bin_mask_key = six.b(mask_key)\n\n    if isinstance(data, six.text_type):\n        data = six.b(data)\n\n    _m = array.array("B", bin_mask_key)\n    _d = array.array("B", data)\n    s = _mask(_m, _d)\n\n    if isinstance(mask_key, six.text_type):\n        mask_key = mask_key.encode(\'utf-8\')\n    return mask_key + s\n\n\ndef ws_encode(data="", opcode=OPCODE_TEXT, mask=1):\n    if opcode == OPCODE_TEXT and isinstance(data, six.text_type):\n        data = data.encode(\'utf-8\')\n\n    length = len(data)\n    fin, rsv1, rsv2, rsv3, opcode = 1, 0, 0, 0, opcode\n\n    frame_header = chr(fin << 7 | rsv1 << 6 | rsv2 << 5 | rsv3 << 4 | opcode)\n\n    if length < 0x7e:\n        frame_header += chr(mask << 7 | length)\n        frame_header = six.b(frame_header)\n    elif length < 1 << 16:\n        frame_header += chr(mask << 7 | 0x7e)\n        frame_header = six.b(frame_header)\n        frame_header += struct.pack("!H", length)\n    else:\n        frame_header += chr(mask << 7 | 0x7f)\n        frame_header = six.b(frame_header)\n        frame_header += struct.pack("!Q", length)\n\n    if not mask:\n        return frame_header + data\n    return frame_header + get_masked(data)\n\n\ndef ws_decode(data):\n    """\n    ws frame decode.\n    :param data:\n    :return:\n    """\n    _data = [ord(character) for character in data]\n    length = _data[1] & 127\n    index = 2\n    if length < 126:\n        index = 2\n    if length == 126:\n        index = 4\n    elif length == 127:\n        index = 10\n    return array.array(\'B\', _data[index:]).tostring()\n\n\n# connect\nsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nsock.connect((socket.gethostbyname(\'echo.websocket.org\'), 80))\n\n# handshake\nhandshake = \'GET / HTTP/1.1\\r\\nHost: echo.websocket.org\\r\\nUpgrade: websocket\\r\\nConnection: \' \\\n            \'Upgrade\\r\\nSec-WebSocket-Key: gfhjgfhjfj\\r\\nOrigin: http://example.com\\r\\nSec-WebSocket-Protocol: \' \\\n            \'echo\\r\\n\' \\\n            \'Sec-WebSocket-Version: 13\\r\\n\\r\\n\'\n\nsock.send(handshake)\nprint(sock.recv(1024))\n\nsock.sendall(ws_encode(data=\'Hello, China!\', opcode=OPCODE_TEXT))\n\n# receive it back\nresponse = ws_decode(sock.recv(1024))\nprint(\'--%s--\' % response)\n\nsock.close()\n
Run Code Online (Sandbox Code Playgroud)\n