Search code examples
pythonwebsocketraspbianpython-asyncio

Listen to websockets running Raspbian from Windows


I've created a websocket using Python's asyncio and websockets modules. This servers works properly in the same machine. This is the actual code for the server:

import sys
import os
import asyncio
import websockets

@asyncio.coroutine
def receive(websocket, path):
    data = yield from websocket.recv()
    print('< {}'.format(data))

    output = 'Sent data from server: {}'.format(data)

    yield from websocket.send(output)
    print('> {}'.format(output))


start_server = websockets.serve(receive, '127.0.0.1', 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

It runs properly, and the connection from a client residing on the same machine connects to it without any problem.

But when I try to access it from a client on a LAN network, it generates a ConnectionRefusedError. This is the client code:

import asyncio
import websockets

@asyncio.coroutine
def hello():
    websocket = yield from websockets.connect(
        'ws://192.168.0.26:8765')

    try:
        name = input("What's your name? ")

        yield from websocket.send(name)
        print("> {}".format(name))

        greeting = yield from websocket.recv()
        print("< {}".format(greeting))

    finally:
        yield from websocket.close()

asyncio.get_event_loop().run_until_complete(hello())

I've installed ufw on Raspbian to enable the port 8765 with this command:

ufw allow 8765

But it doesn't work. On the Windows machine, the command

nmap -p 8765 192.168.0.26

generates this result:

PORT        STATE    SERVICE
8765/tcp    closed   ultraseek-http

And... the command

ufw status

Could someone give some suggestions to solve this communication problem between the client and the server.


Solution

  • Here is one problem:

    start_server = websockets.serve(receive, '127.0.0.1', 8765)
    

    You have told websockets to listen only on 127.0.0.1, thus you can only receive connections originating from the local host, and only on legacy IPv4. Both localhost IPv6 connections (the default) and all connections from other computers will receive the connection refused error.

    If you want to receive connections from outside the local machine, you should set the Host to None or the empty string. This will accept connections from anywhere, on both IPv6 and IPv4, subject of course to any firewall rules.

    start_server = websockets.serve(receive, None, 8765)
    

    The host and port are passed directly to asyncio.create_server() which documents Host as:

    • If host is a string, the TCP server is bound to a single network interface specified by host.
    • If host is a sequence of strings, the TCP server is bound to all network interfaces specified by the sequence.
    • If host is an empty string or None, all interfaces are assumed and a list of multiple sockets will be returned (most likely one for IPv4 and another one for IPv6).