M463
M463

Reputation: 2183

Raspi Pico W [Errno 98] EADDRINUSE despite using socket.SO_REUSEADDR

I'm trying to set up a simple server/client connection using the socket module on a Raspberry Pi Pico W running the latest nightly build image rp2-pico-w-20221123-unstable-v1.19.1-713-g7fe7c55bb.uf2 which I've downloaded from https://micropython.org/download/rp2-pico-w/

The following code runs fine for the first connection (network connectivity can be assumed at this point).

import socket

def await_connection():
    print(' >> Awaiting connection ...')
    try:
        host_addr = socket.getaddrinfo('0.0.0.0', 46321)[0][-1]

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(host_addr)
        sock.listen(1)

        con, addr = sock.accept()
        while True:
            # keep receiving commands on the open connection until the client stops sending
            cmd = con.recv(1024)
            if not cmd:
                print(f' >> {addr} disconnected')
                break
            elif cmd.decode() == 'foo':
                response = 'bar'.encode()
            else:
                response = cmd

            print(f"Received  {cmd.decode()}")
            print(f"Returning {response.decode()}")
            con.sendall(response)

    except OSError as e:
        print(f' >> ERROR: {e}')

    finally:
        # appearantly, context managers are currently not supported in MicroPython, therefore the connection is closed manually
        con.close()
        print(' >> Connection closed.')

while True:
    # main loop, causing the program to await a new connection as soon as the previous one is closed
    await_connection()

If the client closes the connection and tries to re-connect, the infamous [Errno 98] EADDRINUSE is thrown:

code failing in MicroPython 20221123

Please note that I've already implemented the sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) statement, as recommended here, but to no avail.

However, if I run the exact same code on a Raspberry Pi 3 B with python 3.7.3 within the same network, everything works as expected - the client can disconnect and reconnect multiple times without issues:

code working in python 3.7.3

How do I get the Pico to reuse the address after the initial connection, just like it is working in python 3.7.3?

Upvotes: 3

Views: 7891

Answers (1)

M463
M463

Reputation: 2183

While I was able to mitigate the reconnection crash by adding a sock.close() statement after the con.close(), the main issue with my code was the structure itself, as Steffen Ullrich pointed out.

The actual fix was to move the operations on the sock object out of the loop.

import socket

def await_connection():
    print(' >> Awaiting connection ...')
    try:

        con, addr = sock.accept()
        while True:
            # keep receiving commands on the open connection until the client stops sending
            cmd = con.recv(1024)
            if not cmd:
                print(f' >> {addr} disconnected')
                break
            else:
                response = cmd

            print(f"Received  {cmd.decode()}")
            print(f"Returning {response.decode()}")
            con.sendall(response)

    except OSError as e:
        print(f' >> ERROR: {e}')

    finally:
        # appearantly, context managers are currently not supported in MicroPython, therefore the connection is closed manually
        con.close()
        print(' >> Connection closed.')

host_addr = socket.getaddrinfo('0.0.0.0', 46321)[0][-1]

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(host_addr)
sock.listen(1)

while True:
    # main loop, causing the program to await a new connection as soon as the previous one is closed
    await_connection()

Upvotes: 4

Related Questions