AsyncIO part 1:
Sockets
Taras Voinarovskyi
Taras Voinarovskyi
What's on todays menu?
What is a Socket in modern systems
How threads work with sockets
How can a single thread work with multiple sockets using select
Ip Packets
Convert host-to-host packet delivery into a process-to-process communication channel
Socket - an interface between an application process and transport layer
Stream Sockets (SOCK_STREAM)
reliable two-way connection
TCP, Unix
Datagram Sockets (SOCK_DGRAM)
unreliable packet type transfer
...
# Echo client program
import socket
HOST = 'daring.cwi.nl' # The remote host
PORT = 50007 # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.send(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
# Echo server program
import socket
HOST = '' # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data: break
conn.send(data)
def recv(sock, recv_length):
if is_disconnected(sock):
return b""
if len(sock._buffer) == 0:
wait_for_data(sock)
assert len(sock._buffer) > 0
res = sock._buffer[:recv_length]
sock._buffer = sock._buffer[recv_length:]
return res
Blocks if no data available in buffer
May return only 1 byte of data
def send(sock, data):
if is_disconnected(data):
raise BrokenPipeError
if len(sock._buffer) == MAX_BUFFER_LEN:
wait_for_send(sock)
assert len(sock._buffer) < MAX_BUFFER_LEN
can_send = MAX_BUFFER_LEN - len(sock._buffer)
if can_send >= len(data): # Full send
sock._buffer += data
return len(data)
else: # Partial send
sock._buffer += data[:can_send]
return can_send
Blocks if buffer has no free space
May send as little at 1 byte from data
readers = {accept_fd}
writers = set()
while True:
r, w, _ = select.select(readers, writers, [])
print("Looping...")
for fd in r:
if fd == accept_fd:
on_accept()
else:
on_read_ready(fd)
for fd in w:
on_write_ready(fd)
def on_accept():
conn, addr = accept_sock.accept()
print('Connected by', addr)
readers.add(conn.fileno())
connected[conn.fileno()] = conn
write_buffers = defaultdict(bytearray)
def on_read_ready(fd):
conn = connected[fd]
data = conn.recv(1024)
print("Received", data)
if not data:
del connected[fd]
readers.remove(fd)
if fd in writers:
writers.remove(fd)
# Process data itself. In our case echo it back
writers.add(fd)
write_buffers[fd] += data
def on_write_ready(fd):
conn = connected[fd]
write_buffer = write_buffers[fd]
sent_bytes = conn.send(write_buffer)
if len(write_buffer) != sent_bytes:
write_buffer[:sent_bytes] = b""
else:
write_buffer[:] = b""
writers.remove(fd)
David Beazley - Python Concurrency From the Ground Up:
https://www.youtube.com/watch?v=MCs5OvhV9S4
AsyncIO documentation: https://docs.python.org/3/library/asyncio.html
AsyncIO pitfalls:
https://www.youtube.com/watch?v=GLN_xo4Awcc
Neet references