Python Forum
Selector file descriptor issue in simple multiclient server program
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Selector file descriptor issue in simple multiclient server program
#1
Hi all, first post here- please feel free to critique my etiquette if necessary.

I've written a simple server program as well as a simple client for it, where the client sends a string of data to the server and the server sends back that same string to the client. The server utilizes the python selectors module and non-blocking sockets; for the client, neither seemed necessary. Both programs are using Python 3.6.9 and are running on Ubuntu Linux 18.04, and they are loosely based on the python socket programming tutorial found at Real Python.

My issue is that when I use a loop to create several client sockets and go through the process of creating, connecting, writing, reading, and closing for each of them, I receive an error when attempting to register one of the socket objects with my selectors object (full error posted below).

If I run my program with a single socket, it completes successfully. I've consulted the selectors module documentation and the aforementioned python socket docs, but I'm not quite sure what might be causing this. I don't know if perhaps I need to incorporate non-blocking sockets into my client class as well.

All code is posted below. This includes my server class, client class, wrapper scripts for each, and full server output + error. Thanks in advance.

My client class is as follows:

Quote:import sys
import socket

SOCK_BUF_SIZE = 2048

class Client():

def __init__(self, rhost, rport):
self.rhost = rhost
self.rport = rport
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.outbound = b''
self.inbound = b''

def connect(self):
print(f"CLIENT connecting on {self.rhost}:{self.rport}")
self.sock.connect_ex((self.rhost, self.rport))
print(f"CLIENT connected with client socket {self.sock.getsockname()}")

def write(self, data):
self.outbound += data
while self.outbound:
print(f"Outbound data: {self.outbound}")
amt_sent = self.sock.send(self.outbound)
print(f"Sent {amt_sent} bytes.")
if amt_sent < 1:
print("An error occurred with sending: No data sent.")
break
else:
self.outbound = self.outbound[amt_sent:]
print(f"Updated outbound data: {self.outbound}")

def read(self):
print(f"Reading data... ")
data_buf = self.sock.recv(SOCK_BUF_SIZE)
print(f"Finished reading data.")
while data_buf:
print(f"Adding {data_buf} to inbound data.")
self.inbound += data_buf
data_buf = self.sock.recv(SOCK_BUF_SIZE)
print("Total data received: ", repr(self.inbound))
print("Communication complete!")
self.inbound = b''

def close(self):
print(f"Closing socket connection: {self.sock.getsockname()}")
self.sock.close()



My server class is as follows:

Quote:import socket
import types
import selectors as sels

SOCK_BUF_SIZE = 2048

class Server:

def __init__(self, host, port):
self.host = host
self.port = port
self.selector = sels.DefaultSelector()

def handle_new(self, sock):
accept_sock, addr = sock.accept()
print(f"Handling new connection from {addr}")
accept_sock.setblocking(False)

data = types.SimpleNamespace(address=addr, inbound=b'', outbound=b'')
self.selector.register(accept_sock, sels.EVENT_READ | sels.EVENT_WRITE, data)

def handle_read(self, sock, data):
print(f"Reading data from {sock.getsockname()}")
data.inbound = sock.recv(SOCK_BUF_SIZE)
if data.inbound:
print(f"Received data from socket: {data.inbound}")
data.outbound += data.inbound
data.inbound = b''
print(f"New outbound data: {data.outbound}")
else:
print(f"Connection closed by {sock.getsockname()}. Closing...")
self.selector.unregister(sock)
sock.close()

def handle_write(self, sock, data):
if data.outbound:
print(f"Writing data to {sock.getsockname()}...")
print(f"Data being written: {data.outbound}")
amt_sent = sock.send(data.outbound)
print(f"Sent {amt_sent} bytes.")
data.outbound = data.outbound[amt_sent:]
print(f"Remaining data outbound: {data.outbound}")
else:
print(f"No more data to send. Closing connection!")
sock.close()


def run(self):
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
lsock.bind((self.host, self.port))
lsock.listen()
print(f"SERVER Listening on {self.host}:{self.port}")

lsock.setblocking(False)
self.selector.register(lsock, sels.EVENT_READ, data=None)

while True:
events = self.selector.select(timeout=None)
for key, eventmask in events:
sock = key.fileobj
data = key.data

if (eventmask & sels.EVENT_READ) and not data:
self.handle_new(sock)
elif (eventmask & sels.EVENT_READ):
self.handle_read(sock, data)
elif (eventmask & sels.EVENT_WRITE):
self.handle_write(sock, data)

Here is the script I run to start the server:

Quote:import aeserver
import aeclient

server = aeserver.Server("127.0.0.1", 6432)
server.run()

And here is the script the constructs and runs each client socket in succession:

Quote:import aeserver
import aeclient

client_messages = [b"hello world", b"hello again, world", b"goodbye world" ]

for msg in client_messages:
client = aeclient.Client("127.0.0.1", 6432)
client.connect()
client.write(msg)
client.read()
client.close()

Finally, the full output of the server program:

Quote:SERVER Listening on 127.0.0.1:6432
Handling new connection from ('127.0.0.1', 54138)
Reading data from ('127.0.0.1', 6432)
Received data from socket: b'hello world'
New outbound data: b'hello world'
Writing data to ('127.0.0.1', 6432)...
Data being written: b'hello world'
Sent 11 bytes.
Remaining data outbound: b''
No more data to send. Closing connection!
Handling new connection from ('127.0.0.1', 54140)
Traceback (most recent call last):
File "main.py", line 5, in <module>
server.run()
File "/home/leo/coding/python/aether/aeserver.py", line 64, in run
self.handle_new(sock)
File "/home/leo/coding/python/aether/aeserver.py", line 20, in handle_new
self.selector.register(accept_sock, sels.EVENT_READ | sels.EVENT_WRITE, data)
File "/usr/lib/python3.6/selectors.py", line 405, in register
key = super().register(fileobj, events, data)
File "/usr/lib/python3.6/selectors.py", line 241, in register
.format(fileobj, key.fd))
KeyError: "<socket.socket fd=5, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('127.0.0.1', 6432), raddr=('127.0.0.1', 54140)> (FD 5) is already registered"
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Paramiko Server -- Exception (server): Error reading SSH protocol banner ujlain 3 4,276 Jul-24-2023, 06:52 AM
Last Post: Gribouillis
  Simple TCP Client and TCP Server Problem Vapulabis 5 4,335 Jul-12-2020, 05:09 PM
Last Post: ndc85430
  simple udp server/client cardmaker 2 3,545 Nov-26-2019, 12:36 AM
Last Post: micseydel
  Issue: Script from jumpserver to another server to target device? searching1 0 2,067 May-29-2019, 03:43 AM
Last Post: searching1
  Issue when running telnet program using python3 searching1 11 13,368 Dec-22-2018, 12:48 AM
Last Post: searching1
  tcp server/client port connection issue valshev 2 4,945 Sep-01-2018, 02:50 PM
Last Post: valshev
  Simple send and recive string Server/Client Epilepsy 1 2,713 May-01-2018, 08:17 PM
Last Post: ThiefOfTime
  Simple TCP Server shift838 4 5,534 May-12-2017, 03:35 AM
Last Post: nilamo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020