python-hyper / wsproto

Sans-IO WebSocket protocol implementation
https://wsproto.readthedocs.io/
MIT License
261 stars 38 forks source link

[QUESTION] how to receive messages #155

Closed nskalis closed 3 years ago

nskalis commented 3 years ago

Hi,

and thank you for making this library available.

I am facing a difficulty in consuming messages using wsproto. More specifically,

on server-side 10 messages are sent one after the other:


* **client** (using `wsproto`)

def net_send(out_data, sock): sock.send(out_data)

def net_recv(ws_conn, sock): in_data = sock.recv(4096) if not in_data: ws_conn.receive_data(None) else: ws_conn.receive_data(in_data)

def handle_events(ws_conn): for event in ws_conn.events(): if isinstance(event, AcceptConnection): message = None return (True, message) elif isinstance(event, RejectConnection): message = {"error": event.status_code, "reason": "rejecting connection"} return (False, message) elif isinstance(event, CloseConnection): message = {"error": event.code, "reason": event.reason} sock.send(ws_conn.send(event.response())) return (False, message) elif isinstance(event, TextMessage): message = event.data return (True, message) else: message = {"error": -1, "reason": str(event)} return (False, message)

def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((API_IPADDR, API_PORT)) ws_conn = WSConnection(ConnectionType.CLIENT) cliend_id = str(uuid.uuid4()) endpoint = "/ws/{}".format(cliend_id) net_send(ws_conn.send(Request(host=API_IPADDR, target=endpoint)), sock) net_recv(ws_conn, sock) result, message = handle_events(ws_conn) if not result: print(message) # note: error

return

data = json.dumps({"functionality": "ping", "parameters": {"ipaddr": "1.1.1.1"}})
net_send(ws_conn.send(Message(data=data)), sock)
while True:
    net_recv(ws_conn, sock)
    result, message = handle_events(ws_conn)
    print(message)
    if result:
        pass
    else:
        break

net_send(ws_conn.send(CloseConnection(code=1000, reason="ok")), sock)
net_recv(ws_conn, sock)
try:
    sock.shutdown(socket.SHUT_WR)
    net_recv(ws_conn, sock)
except Exception as e:
    print(repr(e))

What happens when I run the `wsproto`-based client is that it receives the first messages and blocks on `net_recv` I think.
Here is the corresponding output:
...: 

{"part": "data", "data": "xxx"} ^C--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last)

in 15 net_send(ws_conn.send(Message(data=data)), sock) 16 while True: ---> 17 net_recv(ws_conn, sock) 18 result, message = handle_events(ws_conn) 19 print(message) in net_recv(ws_conn, sock) 24 25 def net_recv(ws_conn, sock): ---> 26 in_data = sock.recv(4096) 27 if not in_data: 28 ws_conn.receive_data(None) KeyboardInterrupt: ``` When using the `websockets` library, I see all received messages properly. * **client** (using `websockets`) ``` import asyncio import websockets async def hello(): cliend_id = str(uuid.uuid4()) uri = "ws://127.0.0.1:5048/ws/{}".format(cliend_id) async with websockets.connect(uri) as websocket: data = json.dumps({"functionality": "ping", "parameters": {"ipaddr": "1.1.1.1"}}) await websocket.send(data) while True: message = await websocket.recv() print(message) asyncio.run(hello()) ``` outputs (correctly) the following: ``` {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "data", "data": "xxx"} {"part": "control", "data": null} ``` Can you please advise what I am doing wrong and why I am not receiving all the messages that the server is sending?
Kriechi commented 3 years ago

Looks like you are always returning early from handling the messages, and then skip (loose) the remaining events with the other messages:

def handle_events(ws_conn):
    for event in ws_conn.events():
        if isinstance(event, AcceptConnection):
            message = None
            return (True, message)   <-- DON'T RETURN HERE
...
nskalis commented 3 years ago

thanks Thomas, I think I got it now. :)