My Problem in short: I dont know how the selector knows which socket should read or write first.
It is a Server that can handle multi connections and its flow should be:
- Server creates listening socket
- Client creates 2 sockets and connects them to the server
- Client 2 sockets send the messages
- Server 2 sockets echo those messages, client and server closing connection
which is what happens, but if the created server sockets would write first, the connection would be closed immediately or throw an exception(?), since it doesn’t even call send and the client socket would recv nothing. So how does the selector know which sockets should be ready to write/read first? Which information do i miss to understand this?
Server:
import socket import selectors import types host = "127.0.0.1" port = 63210 def accept_wrapper(sock): conn, addr = sock.accept() print('accepted connection from', addr) conn.setblocking(False) data = types.SimpleNamespace(addr=addr, inb=b'', outb=b'') events = selectors.EVENT_READ | selectors.EVENT_WRITE sel.register(conn, events, data=data) def service_connection(key, mask): sock = key.fileobj data = key.data if mask & selectors.EVENT_READ: recv_data = sock.recv(1024) if recv_data: data.outb += recv_data else: print('closing connection to', data.addr) sel.unregister(sock) sock.close() if mask & selectors.EVENT_WRITE: if data.outb: print('echoing', repr(data.outb), 'to', data.addr) sent = sock.send(data.outb) data.outb = data.outb[sent:] sel = selectors.DefaultSelector() lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) lsock.bind((host, port)) lsock.listen() print('listening on', (host, port)) lsock.setblocking(False) sel.register(lsock, selectors.EVENT_READ, data=None) while True: events = sel.select(timeout=None) for key, mask in events: if key.data is None: accept_wrapper(key.fileobj) else: service_connection(key, mask)
Client:
import socket import selectors import types host = "127.0.0.1" port = 63210 num_conns = 2 messages = [b'Message 1 from client.', b'Message 2 from client.'] def start_connections(host, port, num_conns): server_addr = (host, port) for i in range(0, num_conns): connid = i + 1 print('starting connection', connid, 'to', server_addr) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) sock.connect_ex(server_addr) events = selectors.EVENT_READ | selectors.EVENT_WRITE data = types.SimpleNamespace(connid=connid, msg_total=sum(len(m) for m in messages), recv_total=0, messages=list(messages), outb=b'') sel.register(sock, events, data=data) def service_connection(key, mask): sock = key.fileobj data = key.data if mask & selectors.EVENT_READ: recv_data = sock.recv(1024) if recv_data: print('received', repr(recv_data), 'from connection', data.connid) data.recv_total += len(recv_data) if not recv_data or data.recv_total == data.msg_total: print('closing connection', data.connid) sel.unregister(sock) sock.close() if mask & selectors.EVENT_WRITE: if not data.outb and data.messages: data.outb = data.messages.pop(0) if data.outb: print('sending', repr(data.outb), 'to connection', data.connid) sent = sock.send(data.outb) data.outb = data.outb[sent:] sel = selectors.DefaultSelector() start_connections(host, port, num_conns) while True: events = sel.select(timeout=None) for key, mask in events: service_connection(key, mask)
Advertisement
Answer
Sockets don’t actually write directly to the peer and they don’t read from the peer. Instead they write into a local socket specific write buffer and read from a socket specific read buffer. The OS kernel cares about the delivery of the data from the socket write buffer to the peer and puts received packets from the peer into the sockets receive buffer.
The status of these in-kernel socket buffers and changes to these buffers can be monitored with functions like select
, poll
, kqueue
. In essence: a socket is considered writable if there is room in the sockets write buffer. A socket is considered readable if there are data in the sockets read buffer.