2017-05-02 3 views
5

간단한 WebSocket 클라이언트를 작성했습니다. 내가 찾은 코드를 여기에 사용했습니다 : How can I send and receive WebSocket messages on the server side?.Python 소켓/WebSocket 클라이언트를 통해 WebSocket 메시지 보내기/받기

나는 Python 2.7이고 내 서버는 echo.websocket.org입니다. 80 TCP 포트입니다. 기본적으로 메시지를받는 데 문제가 있다고 생각합니다. (아니면 전송도 잘못?) 나는 좋은 핸드 쉐이크 응답을받을 수 있기 때문에

가 적어도 나는, 핸드 셰이크 모든 괜찮 확신 :

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

코드 정의와 코드를 기반으로하는 코드의 근본적인 차이점은 디코딩 정의에 있습니다. 단순히 byte를 변환하기 만하면 byteArray = stringStreamIn 입력을 변환하지 않는다. 원래 코드는 전체 입력 문자열'byteArray = [stringStreamIn에있는 문자의 ord (문자)]을 변환합니다. –

답변

1

https://tools.ietf.org/html/rfc6455#section-5.1에 부합 함 :

클라이언트 프레임을 마스킹해야합니다. (그리고 서버 프레임은 전혀 마스크되지 않습니다.)

  • 는 클라이언트가 서버 (자세한 내용은 5.3 절 참조)로 전송하는 모든 프레임을 마스크해야합니다. WebSocket 프로토콜이 TLS를 통해 을 실행하는지 여부와 상관없이 마스킹이 수행됩니다. 서버는 마스킹되지 않은 프레임을 수신하면 연결을 닫아야합니다. 이 경우 서버는 7.4.1 절에 정의 된대로 상태 코드 1002 (프로토콜 오류) 인 닫기 프레임을 보낼 수 있습니다. 서버는 클라이언트에게 에게 보내는 프레임을 마스크해서는 안됩니다.이 마스크 프레임을 감지하면 클라이언트는 접속을 종료해야

을이 작업 버전이다.

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 (마스크 << 7 | 길이)'등. 해야 할 것? – yak

+0

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

+0

@yak 5.2를 참조하십시오. 내가 제공 한 링크의 기본 프레이밍 프로토콜 –

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이 한 것처럼 일부 추가 라이브러리를 사용하지 않고 코딩 할 수 있습니다.

관련 문제