|
| 1 | +## Simple TCP/IP server that accepts both IPv4 and IPv6 connections |
| 2 | +import chronos |
| 3 | + |
| 4 | +# Because handleConn runs as an indepented task via `asyncSpawn`, it must |
| 5 | +# handle all errors on its own (or the application will crash) |
| 6 | +proc handleConn(transport: StreamTransport, done: Future[void]) {. |
| 7 | + async: (raises: []).} = |
| 8 | + # Handle a single remote connection |
| 9 | + try: |
| 10 | + echo "Incoming connection from ", transport.remoteAddress() |
| 11 | + |
| 12 | + while true: |
| 13 | + # Read and echo back lines until `q` or `0` is entered |
| 14 | + let data = await transport.readLine(sep="\n") |
| 15 | + if data.len > 0: |
| 16 | + if data[0] == 'q': |
| 17 | + if not done.finished(): |
| 18 | + # Notify server that it's time to stop - some other client could |
| 19 | + # have done this already, so we check with `finished` first |
| 20 | + done.complete() |
| 21 | + break |
| 22 | + elif data[0] == '0': |
| 23 | + break # Stop reading and close the connection |
| 24 | + |
| 25 | + echo "Echoed ", await transport.write(data & "\n"), " bytes" |
| 26 | + except CancelledError: |
| 27 | + raiseAssert "No cancellations in this example" |
| 28 | + except TransportError as exc: |
| 29 | + echo "Connection problem! ", exc.msg |
| 30 | + finally: |
| 31 | + # Connections must always be closed to avoid resource leaks |
| 32 | + await transport.closeWait() |
| 33 | + |
| 34 | +proc myApp(server: StreamServer) {.async.} = |
| 35 | + echo "Accepting connections on ", server.local |
| 36 | + let done = Future[void].init() |
| 37 | + try: |
| 38 | + while true: |
| 39 | + let |
| 40 | + accept = server.accept() |
| 41 | + |
| 42 | + # Wait either for a new connection or that an existing connection signals |
| 43 | + # that it's time to stop |
| 44 | + discard await race(accept, done) |
| 45 | + if done.finished(): break |
| 46 | + |
| 47 | + # asyncSpawn is used to spawn an independent async task that runs until |
| 48 | + # it's finished without blocking the current async function - the OS will |
| 49 | + # clean up these tasks on shutdown but a long-running server will want to |
| 50 | + # collect them and make sure their associated sockets are closed properly |
| 51 | + asyncSpawn handleConn(accept.read(), done) |
| 52 | + finally: |
| 53 | + await server.closeWait() |
| 54 | + |
| 55 | +let |
| 56 | + # This server listens on both IPv4 and IPv6 and will pick a port number by |
| 57 | + # itself |
| 58 | + server = createStreamServer(AnyAddress6) |
| 59 | + |
| 60 | +waitFor myApp(server) |
0 commit comments