2017-05-02 14 views
5

私は簡単なWebSocketクライアントを作成しました。私はSOで見つけたコードを使用しました。ここではHow can I send and receive WebSocket messages on the server side?です。Pythonソケット/ WebSocketクライアントでWebSocketメッセージを送受信する

私はPython 2.7を使用しており、私のサーバーはecho.websocket.org80のTCPポートです。基本的には、メッセージの受信に問題があると思います。 (それとも送信はあまりにも間違っている?)私は良いハンドシェイク応答を受信するので

は、少なくとも私は、握手がすべてOKであることを確信しています:

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でビルドしようとしました。以下のテストのために

、私は自分のブラウザを使用:クライアントからサーバーへ

enter image description here

仮面データ、::

enter image description here

サーバからクライアントへのデータを、マスクされていません

+0

ソケットレイヤーのレベルは**高**です。再構成のためにすべてのヘッダーにアクセスすることはできません。ソケット接続用にTCPまたはUDPのみを選択してください。 – dsgdfg

+0

@dsgdfg:私はあなたを理解していないと思う。私は 'echo.websocket.org'サーバにメッセージを送りたいだけです。私はヘッダーを変更したくない、単にこのサーバーで使用されているようにヘッダーを作成するだけです。 Wiresharkをどのように使うべきかを確認し、同じパケットをPythonでビルドしようとしました。私の編集を見てください。 – yak

+0

デコードの定義には、コードとそのコードに基づいた基本的な違いがあります。代わりに、単一文字を長さに変換するだけではなく、入力 'byteArray = stringStreamIn'を変換しません。元のコードは入力文字列 'byteArray = [stringStreamInの文字のord(文字)]を変換します。 –

答えて

1

https://tools.ietf.org/html/rfc6455#section-5.1に:

クライアントフレームをマスクする必要があります。 (およびサーバーフレームは全くマスクされていません。)

  • は、クライアントが はサーバー(詳細は5.3節を参照)に送信するすべてのフレームをマスクしなければなりません。 WebSocketプロトコルがTLS上で を実行しているかどうかにかかわらず、マスキングが行われることに注意してください( を参照してください)。サーバは、マスクされていない フレームを受信すると接続を閉じる必要があります。この場合、サーバは、 セクション7.4.1で定義されているように、ステータスコード1002(プロトコルエラー)を持つClose フレームを送信することができる(MAY)。 サーバはクライアントに に送信するフレームをマスクしてはいけません。それがマスクされた フレームを検出した場合クライアントが接続を閉じる必要があります

これは作業バージョンです。私は、少なくとも応答を送信何かにあなたのコードをハッキングしている

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() 
+0

ありがとうございます。それは今、完璧に動作します。しかし、私はいくつかのコード部分を理解していない。お手伝いできますか?これは: 'frame_header = chr(fin << 7 | rsv1 << 6 | rsv2 << 5 | rsv3 << 4 | opcode)'とこれは 'chr(mask << 7 | length)'などです。行う? – yak

+0

@yak https://tools.ietf.org/html/rfc6455#section-5.2 – gushitong

+0

@yak 5.2を参照してください。私が提供したリンクのBase Framing Protocol –

1

chr()を使用して、ヘッダーに小数点以下のバイト文字列を挿入するようにエンコードを変更して、回答を受け取ります。私が残したデコードは、ここでの他の答えは解決策があります。この
本当の根性は、それはあなたがしなければならないということですまさにその詳細はここではhttps://www.rfc-editor.org/rfc/rfc6455.txt
を詳述されている

#!/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() 

結果:

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 

私はこれがしようとしていることを、ここで注意しなければなりません@gushitongが行ったように、いくつかの余分なライブラリを引っ張らずにコードすることができます。

関連する問題