1Using sockets and streams 2========================= 3 4.. py:currentmodule:: anyio 5 6Networking capabilities are arguably the most important part of any asynchronous library. 7AnyIO contains its own high level implementation of networking on top of low level primitives 8offered by each of its supported backends. 9 10Currently AnyIO offers the following networking functionality: 11 12* TCP sockets (client + server) 13* UNIX domain sockets (client + server) 14* UDP sockets 15 16More exotic forms of networking such as raw sockets and SCTP are currently not supported. 17 18.. warning:: Unlike the standard BSD sockets interface and most other networking libraries, AnyIO 19 (from 2.0 onwards) signals the end of any stream by raising the 20 :exc:`~EndOfStream` exception instead of returning an empty bytes object. 21 22Working with TCP sockets 23------------------------ 24 25TCP (Transmission Control Protocol) is the most commonly used protocol on the Internet. It allows 26one to connect to a port on a remote host and send and receive data in a reliable manner. 27 28To connect to a listening TCP socket somewhere, you can use :func:`~connect_tcp`:: 29 30 from anyio import connect_tcp, run 31 32 33 async def main(): 34 async with await connect_tcp('hostname', 1234) as client: 35 await client.send(b'Client\n') 36 response = await client.receive() 37 print(response) 38 39 run(main) 40 41As a convenience, you can also use :func:`~connect_tcp` to establish a TLS session with the 42peer after connection, by passing ``tls=True`` or by passing a nonempty value for either 43``ssl_context`` or ``tls_hostname``. 44 45To receive incoming TCP connections, you first create a TCP listener with 46:func:`create_tcp_listener` and call :meth:`~.abc.Listener.serve` on it:: 47 48 from anyio import create_tcp_listener, run 49 50 51 async def handle(client): 52 async with client: 53 name = await client.receive(1024) 54 await client.send(b'Hello, %s\n' % name) 55 56 57 async def main(): 58 listener = await create_tcp_listener(local_port=1234) 59 await listener.serve(handle) 60 61 run(main) 62 63See the section on :ref:`TLS` for more information. 64 65Working with UNIX sockets 66------------------------- 67 68UNIX domain sockets are a form of interprocess communication on UNIX-like operating systems. 69They cannot be used to connect to remote hosts and do not work on Windows. 70 71The API for UNIX domain sockets is much like the one for TCP sockets, except that instead of 72host/port combinations, you use file system paths. 73 74This is what the client from the TCP example looks like when converted to use UNIX sockets:: 75 76 from anyio import connect_unix, run 77 78 79 async def main(): 80 async with await connect_unix('/tmp/mysock') as client: 81 await client.send(b'Client\n') 82 response = await client.receive(1024) 83 print(response) 84 85 run(main) 86 87And the listener:: 88 89 from anyio import create_unix_listener, run 90 91 92 async def handle(client): 93 async with client: 94 name = await client.receive(1024) 95 await client.send(b'Hello, %s\n' % name) 96 97 98 async def main(): 99 listener = await create_unix_listener('/tmp/mysock') 100 await listener.serve(handle) 101 102 run(main) 103 104.. note:: The UNIX socket listener does not remove the socket it creates, so you may need to delete 105 them manually. 106 107Sending and receiving file descriptors 108++++++++++++++++++++++++++++++++++++++ 109 110UNIX sockets can be used to pass open file descriptors (sockets and files) to another process. 111The receiving end can then use either :func:`os.fdopen` or :func:`socket.socket` to get a usable 112file or socket object, respectively. 113 114The following is an example where a client connects to a UNIX socket server and receives the 115descriptor of a file opened on the server, reads the contents of the file and then prints them on 116standard output. 117 118Client:: 119 120 import os 121 122 from anyio import connect_unix, run 123 124 125 async def main(): 126 async with await connect_unix('/tmp/mysock') as client: 127 _, fds = await client.receive_fds(0, 1) 128 with os.fdopen(fds[0]) as file: 129 print(file.read()) 130 131 run(main) 132 133Server:: 134 135 from pathlib import Path 136 137 from anyio import create_unix_listener, run 138 139 140 async def handle(client): 141 async with client: 142 with path.open('r') as file: 143 await client.send_fds(b'this message is ignored', [file]) 144 145 146 async def main(): 147 listener = await create_unix_listener('/tmp/mysock') 148 await listener.serve(handle) 149 150 path = Path('/tmp/examplefile') 151 path.write_text('Test file') 152 run(main) 153 154Working with UDP sockets 155------------------------ 156 157UDP (User Datagram Protocol) is a way of sending packets over the network without features like 158connections, retries or error correction. 159 160For example, if you wanted to create a UDP "hello" service that just reads a packet and then 161sends a packet to the sender with the contents prepended with "Hello, ", you would do this:: 162 163 import socket 164 165 from anyio import create_udp_socket, run 166 167 168 async def main(): 169 async with await create_udp_socket(family=socket.AF_INET, local_port=1234) as udp: 170 async for packet, (host, port) in udp: 171 await udp.sendto(b'Hello, ' + packet, host, port) 172 173 run(main) 174 175.. note:: If you are testing on your local machine or don't know which family socket to use, it is 176 a good idea to replace ``family=socket.AF_INET`` by ``local_host='localhost'`` in the 177 previous example. 178 179If your use case involves sending lots of packets to a single destination, you can still "connect" 180your UDP socket to a specific host and port to avoid having to pass the address and port every time 181you send data to the peer:: 182 183 from anyio import create_connected_udp_socket, run 184 185 186 async def main(): 187 async with await create_connected_udp_socket( 188 remote_host='hostname', remote_port=1234) as udp: 189 await udp.send(b'Hi there!\n') 190 191 run(main) 192