Python Socket Programming - Multiple Clients Chat
Learn how to create a group chat using Python sockets. It will consist of a server with multiple clients communicating over Python sockets. We will also learn about multithreading in Python.
Table of Contents 📖
Coding the Server
Lets begin by creating our Server. First lets import the libraries that we will need.
import socket
from threading import Thread
- socket - A low-level networking interface. Allows us to send messages across a network.
- threading - Allows us to run a function in a separate thread of control. We will use this in both our server and client to allow for simultaneous listening and sending of messages.
INFO: Threads are a unit of execution that is scheduled by the operating system and executed by the CPU. Processes are made up of one to many threads.
We will use a class called Server to represent our server. Lets create the constructor of this class. The constructor will create a socket to listen for connections from the client.
class Server:
Clients = []
# Create a TCP socket over IPv4. Accept at max 5 connections.
def __init__(self, HOST, PORT):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((HOST, PORT))
self.socket.listen(5)
print('Server waiting for connection....')
We also create a static list called Clients to store the clients that are connected to the server. Now lets create a function called listen that will listen for connections from the client.
# Listen for connections on the main thread. When a connection
# is received, create a new thread to handle it and add the client
# to the list of clients.
def listen(self):
while True:
client_socket, address = self.socket.accept()
print("Connection from: " + str(address))
# The first message will be the username
client_name = client_socket.recv(1024).decode()
client = {'client_name': client_name, 'client_socket': client_socket}
# Broadcast that the new client has connected
self.broadcast_message(client_name, client_name + " has joined the chat!")
Server.Clients.append(client)
Thread(target = self.handle_new_client, args = (client,)).start()
We need to spin up a thread to handle each client so that we can listen on multiple socket connections. If we didn't do this, we would be blocked on listening for messages from a single client.
def handle_new_client(self, client):
client_name = client['client_name']
client_socket = client['client_socket']
while True:
# Listen out for messages and broadcast the message to all clients.
client_message = client_socket.recv(1024).decode()
# If the message is bye, remove the client from the list of clients and
# close down the socket.
if client_message.strip() == client_name + ": bye" or not client_message.strip():
self.broadcast_message(client_name, client_name + " has left the chat!")
Server.Clients.remove(client)
client_socket.close()
break
else:
# Send the message to all other clients
self.broadcast_message(client_name, client_message)
# Loop through the clients and send the message down each socket.
# Skip the socket if it's the same client.
def broadcast_message(self, sender_name, message):
for client in self.Clients:
client_socket = client['client_socket']
client_name = client['client_name']
if client_name != sender_name:
client_socket.send(message.encode())
Now lets create an instance of this server and have it listen on localhost and port 7632.
if __name__ == '__main__':
server = Server('127.0.0.1', 7632)
server.listen()
Coding the Client
Now lets code our client. First lets import the packages. These will be the same packages on the server with the exception of the os package.
import socket
from threading import Thread
# Provides us with methods for interacting with the operating system.
import os
class Client:
# When creating a client, connect to the server, ask for
# a username, and begin server communication.
def __init__(self, HOST, PORT):
self.socket = socket.socket()
self.socket.connect((HOST, PORT))
self.name = input("Enter your name: ")
self.talk_to_server()
# First send over the name of the client. Then start listening
# for messages on a separate thread. Message sending will be on
# the main thread.
def talk_to_server(self):
self.socket.send(self.name.encode())
Thread(target = self.receive_message).start()
self.send_message()
# Get user input and send the message to the server
# with the client's name prepended.
def send_message(self):
while True:
client_input = input("")
client_message = self.name + ": " + client_input
self.socket.send(client_message.encode())
# Constantly listen out for messages. If the message is response
# from the server is empty, close the program.
def receive_message(self):
while True:
server_message = self.socket.recv(1024).decode()
if not server_message.strip():
os._exit(0)
# Add some color to the console.
print("\033[1;31;40m" + server_message + "\033[0m")
if __name__ == '__main__':
Client('127.0.0.1', 7632)
Running the Program
To run the program, navigate to the server.py file and run it. After the server is up and running, run multiple client.py files.
INFO: Remember that we have set a limit of 5 max connections!
python3 ./server.py
Server waiting for connection....
Connection from: ('127.0.0.1', 64191)
Connection from: ('127.0.0.1', 64654)
python3 ./client.py
Enter your name: WittCode
Michael Jackson has joined the chat!
Michael Jackson: Hey everyone!
What's up MJ? Hee hee!
Michael Jackson: I g2g shamona!
Michael Jackson has left the chat!
python3 ./client.py
Enter your name: Michael Jackson
Hey everyone!
WittCode: What's up MJ? Hee hee!
I g2g shamona!
bye