我写了一个简单的WebSocket客户端。我使用了我在SO上找到的代码,这里:How can I send and receive WebSocket messages on the server side?。
我在Python 2.7
TCP端口上使用echo.websocket.org
而我的服务器是80
。基本上,我认为接收消息时遇到问题。 (或者也许发送错了?)
至少我确信握手一切正常,因为我收到了良好的握手响应:
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
我的代码:
#!/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()
这里有什么问题?握手后它变得复杂了。
dencode_text_msg_websocket
方法返回一个空字符串,但它应该返回我发送给服务器的相同字符串,即hello world!
。
我不想使用库(我知道如何使用它们)。问题是关于在没有库的情况下实现同样的事情,仅使用套接字。
我只想向echo.websocket.org server
发送消息并收到回复,这就是全部。我不想修改标题,只需构建这个服务器使用的标题。我检查了它们应该如何使用Wireshark,并尝试使用Python构建相同的数据包。
对于以下测试,我使用了我的浏览器:
从服务器到客户端的未屏蔽数据:
从客户端到服务器的屏蔽数据:
答案 0 :(得分:1)
收到https://tools.ietf.org/html/rfc6455#section-5.1:
您应该屏蔽客户端框架。 (并且根本没有屏蔽服务器帧。)
这是一个有效的版本:
import os
import array
import six
import socket
import struct
OPCODE_TEXT = 0x1
try:
# If wsaccel is available we use compiled routines to mask data.
from wsaccel.xormask import XorMaskerSimple
def _mask(_m, _d):
return XorMaskerSimple(_m).process(_d)
except ImportError:
# wsaccel is not available, we rely on python implementations.
def _mask(_m, _d):
for i in range(len(_d)):
_d[i] ^= _m[i % 4]
if six.PY3:
return _d.tobytes()
else:
return _d.tostring()
def get_masked(data):
mask_key = os.urandom(4)
if data is None:
data = ""
bin_mask_key = mask_key
if isinstance(mask_key, six.text_type):
bin_mask_key = six.b(mask_key)
if isinstance(data, six.text_type):
data = six.b(data)
_m = array.array("B", bin_mask_key)
_d = array.array("B", data)
s = _mask(_m, _d)
if isinstance(mask_key, six.text_type):
mask_key = mask_key.encode('utf-8')
return mask_key + s
def ws_encode(data="", opcode=OPCODE_TEXT, mask=1):
if opcode == OPCODE_TEXT and isinstance(data, six.text_type):
data = data.encode('utf-8')
length = len(data)
fin, rsv1, rsv2, rsv3, opcode = 1, 0, 0, 0, opcode
frame_header = chr(fin << 7 | rsv1 << 6 | rsv2 << 5 | rsv3 << 4 | opcode)
if length < 0x7e:
frame_header += chr(mask << 7 | length)
frame_header = six.b(frame_header)
elif length < 1 << 16:
frame_header += chr(mask << 7 | 0x7e)
frame_header = six.b(frame_header)
frame_header += struct.pack("!H", length)
else:
frame_header += chr(mask << 7 | 0x7f)
frame_header = six.b(frame_header)
frame_header += struct.pack("!Q", length)
if not mask:
return frame_header + data
return frame_header + get_masked(data)
def ws_decode(data):
"""
ws frame decode.
:param data:
:return:
"""
_data = [ord(character) for character in data]
length = _data[1] & 127
index = 2
if length < 126:
index = 2
if length == 126:
index = 4
elif length == 127:
index = 10
return array.array('B', _data[index:]).tostring()
# 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))
sock.sendall(ws_encode(data='Hello, China!', opcode=OPCODE_TEXT))
# receive it back
response = ws_decode(sock.recv(1024))
print('--%s--' % response)
sock.close()