I made a chatroom with a server and client python files, using Sockets and Threading as a project. I'm interested to acquire advice, and optimizations i could make to my program. Also please give me tips on sockets and threading in general, because I'm fairly new to the topic, and i would want to learn whatever is relevant in sockets in general. Thank you.


import threading

# constants
DISCONNECT_COMMAND = '/disconnect'
FORMAT = 'utf-8'
IP = ''
PORT = 1234
print("[SERVER] Starting Server...")

# creating and listening to socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# clients information
clients = {} # {client_socket: {'username': username, 'address': address}}
messages = [] #

def close_connection(client_socket):
    print(f"[DISCONNECTION] {clients[client_socket]['username']} has disconnected!")
    del clients[client_socket]

def send_message(socket, message):
        socket.send(bytes(f"{len(message):<{HEADER_LENGTH}}", FORMAT))
        socket.send(bytes(message, FORMAT))
        print(f"[ERROR] SENDING '{message}' to {clients[socket]['username']}")

def receive_message(client_socket):
    while True:
        message_length = client_socket.recv(HEADER_LENGTH).decode(FORMAT)
        message = client_socket.recv(int(message_length)).decode(FORMAT)
        return message

def send_to_all_clients(message, sender_socket):
    user_message = f"{clients[sender_socket]['username']} > {message}"
    for client_socket in clients:
        if sender_socket != client_socket:
            send_message(client_socket, user_message)

def handle_client(client_socket, client_address):
    # user name and server prompt for new connections
    username = receive_message(client_socket)
    clients[client_socket] = {'username': username, 'address': client_address}
    print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")
    print(f"[NEW CONNECTION] {username} has connected!")

    # server prompt for connection, and receiving previous messages of server.
    send_message(client_socket, f'You have connected to the server with the username "{clients[client_socket]["username"]}"!')
    send_message(client_socket, str(len(messages))) # sending of previous messages to new client
    for i in range(len(messages)):
        send_message(client_socket, messages[i])

    # receiving messages and sending it to other clients
    connected = True
    while connected:
            message = receive_message(client_socket)
            if message == DISCONNECT_COMMAND:
                connected = False

            send_to_all_clients(message, client_socket)

            connected = False


# listening for clients
print("[LISTENING] Listening for Clients")
while True:
    client_socket, client_address = server_socket.accept()
    handling_thread = threading.Thread(target=handle_client, args=[client_socket, client_address])


import socket
import threading

# Server Information
SERVER_ADDRESS = ('', 1234)
FORMAT = 'utf-8'
DISCONNECT_COMMAND = '/disconnect'

def send_message(message):
    message_length = f"{len(message):<{HEADER_LENGTH}}"
    client_socket.send(bytes(message_length, FORMAT))
    client_socket.send(bytes(message, FORMAT))

def receive_message():
    while True:
        message_length = client_socket.recv(HEADER_LENGTH).decode(FORMAT)
        if message_length:
            message = client_socket.recv(int(message_length)).decode(FORMAT)
            return message

def receive_messages_in_real_time():
    while True:
        message = receive_message()

# Connecting to Server
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Username Prompt
username = input("Enter your username:\n")

# Server Message for successful connection, and previous messages in server
number_of_messages = receive_message()
for _ in range(int(number_of_messages)):

# thread for receiving messages
receiving_thread = threading.Thread(target=receive_messages_in_real_time)
receiving_thread.daemon = True

# messaging in the server
connected = True
while connected:
    message = input()
    if message == DISCONNECT_COMMAND:
        connected = False


2 Answers 2


Looks fine, I have no tips. You did good. In a real application, you might want to tuck things into classes or code more defensively. Make sure you deal with frequent client disconnects (they don't properly close the connection) in a way where the server doesn't eventually die.

If you're looking for challenges, you could:

  • Write a class which lets someone write a client bot, then write a small bot using it. I suggest event handlers or callbacks.
  • Do the same thing again, UDP
  • Learn how to test your TCP scaling, up to at least 100-1K clients. Then improve TCP scaling to that range.
  • When it gets big enough, your limit will be traffic. Add individual chat rooms. in groups of 10 people per chat room. Now in theory, it should be no problem to handle 1 message per second for the whole network. Actually support 10K clients.

Optimization on the server side:

  1. In the function send_message - merge the 2 calls to socket.send to one call to socket.send. Also it will be better to replace the call for socket.send with socket.sendall as it will deal better with some problems.

  2. I don't see a reason to send the Header length in UTF-8, it can be a simple number so you will save the decoding time (which is negligible).

  3. In the function receive_message there is no need for while True as the function will always return after the first iteration.

  4. Replace threading with multi-processing as Python GIL will block your threads.

Your client has similar issues - so there is no point writing them down.

There are some other things that are not related to what you asked to be reviewed that I would improve - Type hinting, move your code to functions (mostly in the client), dealing with errors and communicating them (logging) and so on.

Overall it's good code.

  • \$\begingroup\$ It's generally best to send the header length as characters - that allows better portability with other implementations (no need to worry about endianness etc). UTF-8 is a good choice for that (as is ASCII, given it's all digits - in which case the two are the same). \$\endgroup\$ Commented Feb 12, 2022 at 23:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.