Python Socket Programming - Client Server Messenger
Learn how to create a client server chat application using Python sockets. We will also learn about Python threading.
Table of Contents 📖
Coding the Server
Lets start by coding the server. First, lets import the required libraries.
import socket
from threading import Thread
import os
- 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.
- os - Provides us with methods for interacting with the operating system.
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:
def __init__(self, HOST, PORT):
# Create a new socket. AF_INET is the address family for IPv4.
# SOCK_STREAM is the socket type for TCP.
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((HOST, PORT))
# Enable a server to accept connections.
self.socket.listen()
print('Server waiting for connection....')
# Accept a connection. Returns (conn, address). Conn is a new
# socket object used to send and receive data on the connection.
# Address is the address of the other connection.
client_socket, address = self.socket.accept()
print("Connection from: " + str(address))
self.talk_to_client(client_socket)
Now lets code the talk_to_client method. This method will handle both receiving and sending messages to the client.
def talk_to_client(self, client_socket):
# Create a thread and start the thread's activity.
Thread(target = self.receive_message, args = (client_socket,)).start()
self.send_message(client_socket)
We need to create a thread so that we can send and receive messages at the same time. If we don't set up a thread to receive messages, we won't be able to send messages as our server will constantly be listening for messages. Now lets create the send_message method.
def send_message(self, client_socket):
while True:
server_message = input("")
# The encode function converts the string into bytes so we can send the bytes down the socket.
client_socket.send(server_message.encode())
Now, until the program finishes, we will constantly be waiting for user input before sending it to the client. Now lets create the receive_message method.
def receive_message(self, client_socket):
while True:
# Receive data from the socket. 1024 is the buffer size, the max amount of data to be received at once.
# Returns a bytes object. A returned empty bytes object indicates that the client has disconnected.
client_message = client_socket.recv(1024).decode()
if (client_message.strip() == "bye" or not client_message.strip()):
os._exit(0)
# Add a red color to the client message
print("\033[1;31;40m" + "Client: " + client_message + "\033[0m")
This method constantly listens for data on the client socket. When it receives it, it prints it to the screen. If the message is "bye", the program will exit. Now we just need to create an instance of this server.
Server('127.0.0.1', 7632)
Now our server will be listening on localhost port 7632. We need to make sure that our client connects to this location.
Coding the Client
Now lets code the client. The imported libraries will be the same.
import socket
from threading import Thread
import os
Now lets code the constructor of the Client class.
class Client:
def __init__(self, HOST, PORT):
self.socket = socket.socket()
self.socket.connect((HOST, PORT))
self.talk_to_server()
This constructor creates a socket object and then connects to the server on the provided HOST and PORT. This will be localhost 9632. Now lets create the talk_to_server method, this will be similar to the talk_to_client method.
# Listen to messages while also sending messages
def talk_to_server(self):
Thread(target = self.receive_message).start()
self.send_message()
Once again we spin up a separate thread so we can listen for messages and send messages at the same time. Now lets create the send and receive methods. These will both be similar to the methods of the server.
def send_message(self):
while True:
client_message = input("")
self.socket.send(client_message.encode())
def receive_message(self):
while True:
# Calling decode() on None causes Bad file descriptor
server_message = self.socket.recv(1024).decode()
if (server_message.strip() == "bye" or not server_message.strip()):
os._exit(0)
print("\033[1;31;40m" + "Server: " + server_message + "\033[0m")
Finally lets spin up an instance of this client.
Client('127.0.0.1', 7632)
Running the Program
Now lets run the program. To do this, first run the server and then the client.
ERROR: The server needs to be ran first as the client is attempting to connect to it in its constructor.
python3 ./server.py
Server waiting for connection....
Connection from: ('127.0.0.1', 63276)
hi
how are you doing?
Client: i am fine thank you
Client: how are you?
python3 ./client.py
Server: hi
Server: how are you doing?
i am fine thank you
how are you?