Sending / Receiving Websocket Message Over Python Socket / Websocket Client
I wrote a simple WebSocket client. I used the code I found on SO, here: How can I send and receive WebSocket messages on the server side?. I'm using Python 2.7 and my server is ec
Solution 1:
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. 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
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.1101WebSocketProtocolHandshakeAccess-Control-Allow-Credentials:trueAccess-Control-Allow-Headers:content-typeAccess-Control-Allow-Headers:authorizationAccess-Control-Allow-Headers:x-websocket-extensionsAccess-Control-Allow-Headers:x-websocket-versionAccess-Control-Allow-Headers:x-websocket-protocolAccess-Control-Allow-Origin:http://example.comConnection:UpgradeDate:Mon,08May2017 15:08:33 GMTSec-WebSocket-Accept:HSmrc0sMlYUkAGmm5OPpG2HaGWk=Server:KaazingGatewayUpgrade:websocketSent:'\x81MNow is the winter of our discontent, made glorious Summer by this son of York'Received:Nowisthewinterofourdiscontent,madegloriousSummerbythissonofYork
I should note here, that this is going to be a pig to code without pulling in some extra libraries, as @gushitong has done.
Solution 2:
Accoding to https://www.rfc-editor.org/rfc/rfc6455#section-5.1:
You should mask the client frames. (And the server frames is not masked at all.)
- a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). (Note that masking is done whether or not the WebSocket Protocol is running over TLS.) The server MUST close the connection upon receiving a frame that is not masked. In this case, a server MAY send a Close frame with a status code of 1002 (protocol error) as defined in Section 7.4.1. A server MUST NOT mask any frames that it sends to the client. A client MUST close a connection if it detects a masked frame。
This is a working version:
import os
import array
import six
import socket
import struct
OPCODE_TEXT = 0x1try:
# 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 inrange(len(_d)):
_d[i] ^= _m[i % 4]
if six.PY3:
return _d.tobytes()
else:
return _d.tostring()
defget_masked(data):
mask_key = os.urandom(4)
if data isNone:
data = ""
bin_mask_key = mask_key
ifisinstance(mask_key, six.text_type):
bin_mask_key = six.b(mask_key)
ifisinstance(data, six.text_type):
data = six.b(data)
_m = array.array("B", bin_mask_key)
_d = array.array("B", data)
s = _mask(_m, _d)
ifisinstance(mask_key, six.text_type):
mask_key = mask_key.encode('utf-8')
return mask_key + s
defws_encode(data="", opcode=OPCODE_TEXT, mask=1):
if opcode == OPCODE_TEXT andisinstance(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)
ifnot mask:
return frame_header + data
return frame_header + get_masked(data)
defws_decode(data):
"""
ws frame decode.
:param data:
:return:
"""
_data = [ord(character) for character in data]
length = _data[1] & 127
index = 2if length < 126:
index = 2if length == 126:
index = 4elif length == 127:
index = 10return 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()
Post a Comment for "Sending / Receiving Websocket Message Over Python Socket / Websocket Client"