I have the following situation:
I have a class called Server, which is, as the name tells, the class which starts the server and is supposed to stop it. Here's the code:
import socket
import threading
class Server:
def __init__(self, server_manager, host, port, running):
# Define the host and port to listen on
self.host = host
self.port = port
self.server_manager = server_manager
# setting stop event up
self.running = running
# Create a socket object
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the host and port
self.server.bind((self.host, self.port))
# Start listening for up to 5 incoming connections
self.backlog = 5
self.server.listen(self.backlog)
def run(self):
while not self.running.is_set():
try:
# Accept a connection from a client
client_socket, client_address = self.server.accept()
client_handler = threading.Thread(target=self.handle_client, args=(client_socket,))
client_handler.start()
except socket.error:
pass
def handle_client(self, client_socket):
while not self.running.is_set():
try:
client_data = client_socket.recv(1024) # Receive data from the client
if not client_data:
break
# Process the received data here
self.server_manager.show_data(client_data)
# Send a response back to the client (optional)
response = "Successfully sent data! Now responding!"
client_socket.send(response.encode('utf-8'))
except Exception as e:
print(f"Error: {e}")
finally:
client_socket.close()
client_socket.close()
def close(self):
# close
pass
I also have a class called ServerManager, which processes the data coming from the server. Again the code:
import threading
from ConnectionProcessor.connection import Server
class ServerManager:
def __init__(self, host="127.0.0.1", port=6969):
self.host = host
self.port = port
self.running = threading.Event()
self.server = Server(server_manager=self, host=self.host, port=self.port, running=self.running)
self.server_thread = threading.Thread(target=self.server.run)
self.server_thread.start()
def close(self):
self.running.set()
def show_data(self, data: str):
print(data)
It works well, I can print the data coming from the server. My only problem is, that I can't close the server from my main class:
from ConnectionProcessor import ServerManager
from time import sleep
if __name__ == "__main__":
server_manager = ServerManager()
sleep(4)
server_manager.close()
The server is still up and running.
I tried multiple things, also I tried changing self.running from threading.Event to a simple bool, but it has the same effect, it doesn't work.
This is my first time working with threads and socket, maybe it's just a simple mistake.
The problem is socket.accept
doesn't return until a client connects, so the while loop condition won't check the running
event without a client connecting. Set a timeout on the server socket so accept
periodically returns:
def run(self):
while not self.running.is_set():
try:
# Accept a connection from a client
self.server.settimeout(1)
client_socket, client_address = self.server.accept()
except TimeoutError as e:
continue
client_handler = threading.Thread(target=self.handle_client, args=(client_socket,))
client_handler.start()
Another option is to use select.select
with a timeout to see if there is a waiting connection on the server:
import select
...
def run(self):
while not self.running.is_set():
readable, _, _ = select.select([self.server], [], [], 1.0)
if readable:
client_socket, client_address = self.server.accept()
client_handler = threading.Thread(target=self.handle_client, args=(client_socket,))
client_handler.start()