[英]Sending / receiving WebSocket message over Python socket / WebSocket Client
I wrote a simple WebSocket client. 我写了一个简单的WebSocket客户端。 I used the code I found on SO, here: How can I send and receive WebSocket messages on the server side?
我使用了在SO上找到的代码,在这里: 如何在服务器端发送和接收WebSocket消息? .
。
I'm using Python 2.7
and my server is echo.websocket.org
on 80
TCP port. 我正在使用
Python 2.7
,我的服务器是80
TCP端口上的echo.websocket.org
。 Basically, I think that I have a problem with receiving messages. 基本上,我认为我在接收邮件时遇到问题。 (Or maybe the sending is wrong too?)
(或者发送错误吗?)
At least I am sure that the handshake is all ok, since I receive a good handshake response: 至少我确信握手一切正常,因为我收到了良好的握手响应:
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
And my code: 而我的代码:
#!/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()
What is wrong here? 怎么了 It gets complicated after the handshake.
握手后变得复杂。
The dencode_text_msg_websocket
method returns an empty string but it should return the same string I send to the server, which is hello world!
dencode_text_msg_websocket
方法返回一个空字符串,但应返回我发送到服务器的相同字符串,这是hello world!
. 。
I DO NOT WANT to use libraries (I know how to use them). 我不想使用库(我知道如何使用它们)。 The question is about achieving the same thing WITHOUT libraries, using only sockets.
问题是只使用套接字就可以在没有库的情况下实现相同的目的。
I only want to send a message to echo.websocket.org server
and receive a response, that's all. 我只想向
echo.websocket.org server
发送一条消息并接收响应,仅此而已。 I do not want to modify the headers, just build the headers like they're used by this server. 我不想修改标头,只需构建标头,就像该服务器使用的标头一样。 I checked how they should look like using Wireshark, and tried to build the same packets with Python.
我检查了使用Wireshark的外观,并尝试使用Python构建相同的数据包。
For tests below, I used my browser: 对于以下测试,我使用了浏览器:
Not masked data, from server to client: 未屏蔽的数据,从服务器到客户端:
Masked data, from client to server: 从客户端到服务器的屏蔽数据:
I have hacked your code into something that at least sends a reply and receives an answer, by changing the encoding to use chr()
to insert byte strings rather than decimals to the header. 我通过将编码更改为使用
chr()
在标头中插入字节字符串而不是十进制,从而将您的代码破解为至少可以发送答复和接收答案的内容。 The decoding I have left alone but the other answer here has a solution for that. 我不理会解码,但是这里的另一个答案对此有解决方案。
The real guts of this is detailed here https://www.rfc-editor.org/rfc/rfc6455.txt 真正的勇气在这里详细介绍https://www.rfc-editor.org/rfc/rfc6455.txt
which details exactly what it is that you have to do 其中详细说明了您必须执行的操作
#!/usr/bin/env python
import socket
def encode_text_msg_websocket(data):
bytesFormatted = []
bytesFormatted.append(chr(129))
bytesRaw = data.encode()
bytesLength = len(bytesRaw)
if bytesLength <= 125:
bytesFormatted.append(chr(bytesLength))
elif 126 <= bytesLength <= 65535:
bytesFormatted.append(chr(126))
bytesFormatted.append((chr(bytesLength >> 8)) & 255)
bytesFormatted.append(chr(bytesLength) & 255)
else:
bytesFormatted.append(chr(127))
bytesFormatted.append(chr((bytesLength >> 56)) & 255)
bytesFormatted.append(chr((bytesLength >> 48)) & 255)
bytesFormatted.append(chr((bytesLength >> 40)) & 255)
bytesFormatted.append(chr((bytesLength >> 32)) & 255)
bytesFormatted.append(chr((bytesLength >> 24)) & 255)
bytesFormatted.append(chr((bytesLength >> 16)) & 255)
bytesFormatted.append(chr((bytesLength >> 8)) & 255)
bytesFormatted.append(chr(bytesLength) & 255)
send_str = ""
for i in bytesFormatted:
send_str+=i
send_str += bytesRaw
return send_str
# connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
try:
sock.connect((socket.gethostbyname('ws.websocket.org'), 80))
except:
print "Connection failed"
handshake = '\
GET /echo HTTP/1.1\r\n\
Host: echo.websocket.org\r\n\
Upgrade: websocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\
Origin: http://example.com\r\n\
WebSocket-Protocol: echo\r\n\
Sec-WebSocket-Version: 13\r\n\r\n\
'
sock.send(bytes(handshake))
data = sock.recv(1024).decode('UTF-8')
print data
# send test msg
msg = encode_text_msg_websocket('Now is the winter of our discontent, made glorious Summer by this son of York')
print "Sent: ",repr(msg)
sock.sendall(bytes(msg))
# receive it back
response = sock.recv(1024)
#decode not sorted so ignore the first 2 bytes
print "\nReceived: ", response[2:].decode()
sock.close()
Result: 结果:
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: Mon, 08 May 2017 15:08:33 GMT
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Server: Kaazing Gateway
Upgrade: websocket
Sent: '\x81MNow is the winter of our discontent, made glorious Summer by this son of York'
Received: Now is the winter of our discontent, made glorious Summer by this son of York
I should note here, that this is going to be a pig to code without pulling in some extra libraries, as @gushitong has done. 我应该在这里指出,这就像@gushitong所做的那样,无需编写一些额外的库就可以编写代码。
Accoding to https://tools.ietf.org/html/rfc6455#section-5.1 : 编码为https://tools.ietf.org/html/rfc6455#section-5.1 :
You should mask the client frames. 您应该掩盖客户端框架。 (And the server frames is not masked at all.)
(并且服务器框架完全没有被屏蔽。)
This is a working version: 这是一个工作版本:
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.