4

Code

import trio
from trio import socket

async def listen(host, port):
   while True:
        fullmsg = ""
        sock = socket.socket()
        await sock.bind((host, port))
        sock.listen()
        print(f'Awaiting Receive On {host}:{port}')
        conn, addr = await sock.accept()
        print(f'Connection Received From {addr[0]}:{addr[1]}')
        while True:
            try:
                msg = await conn.recv(8)
                if len(msg.decode().strip()) > 0:
                    print(f'Received {len(msg.strip())} bytes')
                    fullmsg += msg.decode().strip()
                else:
                    break
            except Exception as e:
                print(f'DEBUG: {e}')
        sock.shutdown(0)
        sock.close()
        print(fullmsg)


# function that runs the listen function:
async def create():
    async with trio.open_nursery() as nursery:
            nursery.start_soon(listen, '127.0.0.1', 6969)


# To run the program
trio.run(create)

I want to run the function over and over again everytime it receives a message of length 0 or when the connection gets closed by the client but when the function completes the first iteration of the first while loop, it gives an OSError saying the port is already in use. I close and shutdown my sockets by the ending of the loop but I still don't know where the program is erroring out.

Output Of The Program

Awaiting Receive On 127.0.0.1:6969
Connection Received From 127.0.0.1:37122
Received 8 bytes
Received 5 bytes
Hello, World!
Traceback (most recent call last):
  File "./ape.py", line 68, in <module>
    trio.run(create)
  File "/usr/local/lib/python3.8/dist-packages/trio/_core/_run.py", line 1804, in run
    raise runner.main_task_outcome.error
  File "./ape.py", line 59, in create
    nursery.start_soon(listen, '127.0.0.1', 6969)
  File "/usr/local/lib/python3.8/dist-packages/trio/_core/_run.py", line 730, in __aexit__
    raise combined_error_from_nursery
  File "./ape.py", line 15, in listen
    await sock.bind((host, port))
  File "/usr/local/lib/python3.8/dist-packages/trio/_socket.py", line 473, in bind
    return self._sock.bind(address)
OSError: [Errno 98] Address already in use
  • 3
    You need to set the `SO_REUSEADDR` socket option if you want to listen the same address again shortly after it's been un-listened. It would keep open for some time by default to allow any packets still on the way to be handled. – lilydjwg Mar 10 '20 at 11:43
  • 2
    Also recommended, use `trio.serve_tcp` or other higher level APIs because they internally handle quite many issues that you'll run into with bare sockets: https://github.com/python-trio/trio/blob/master/trio/_highlevel_open_tcp_listeners.py#L125 – L. Kärkkäinen Mar 10 '20 at 11:47

1 Answers1

2

Like others said in their comments, the problem is that on Unix-y platforms, you have to set the SO_REUSEADDR socket option if you want to be able to close a listening socket and then immediately open a new one bound to the same port.

Note that on Windows, though, you should never set the SO_REUSEADDR option, because on Windows, the behavior you want is enabled by default, and SO_REUSEADDR is redefined to be a "turn off security" option.

trio.socket is very low-level and exposes all these details, so if you want to deal with them yourself it lets you do that. But most users will be better off using the higher level helpers like trio.serve_tcp, which will handle a lot of these details automatically.

Nathaniel J. Smith
  • 9,038
  • 4
  • 35
  • 46