Skip to main content

📧 Interprocess Communication: Client and Server

🎯 Learning Objective​

  • Understand interprocess communication (IPC) mechanisms using sockets.
  • Create a server and a client in Python.
  • Identify the role of the operating system in managing network connections.

📜 Statement​

In this exercise, you will create a simple communication application between a client and a server using sockets in Python.

🔧 Step 1 – Create a Simple Server​

A server:

  • Opens a "door" (called a port) on your computer.
  • Waits for a client to connect.
  • Receives messages from the client.

Server Code​

Create a file called server.py and copy the following code:

import socket

# Create a TCP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# The IP address and port to use
host = 'localhost'
port = 12345

# Bind the socket to the address
server.bind((host, port))

# Listen for up to 1 connection
server.listen(1)
print("[Server] Waiting for connection...")

# Accept the connection
connection, address = server.accept()
print(f"[Server] Connection from {address}")

# Receive messages
while True:
message = connection.recv(1024).decode()
if message.lower() == "exit":
print("[Server] End of communication.")
break
print(f"[Client] {message}")

# Close the connections
connection.close()
server.close()

Test​

This program does nothing until a client connects. Start the server with the command:

python server.py

and connect with your web browser by going to http://localhost:12345.

tip

You can stop the server by pressing Ctrl+C in the terminal.

💬 Step 2 – Create a Client​

A client:

  • Connects to a server (here via localhost and the port).
  • Sends messages that you type in the console.

2.1 Client Code​

Create a file client.py:

import socket

# Create a TCP socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to the server
client.connect(('localhost', 12345))
print("[Client] Connected to the server. Type your messages:")

# Read and send messages
while True:
message = input(">>> ")
client.sendall(message.encode())
if message.lower() == "exit":
break

# Close the connection
client.close()

2.2 Test​

  1. In one terminal: run python server.py
  2. In another terminal: run python client.py
  3. Exchange a few messages.
  4. Type exit to stop.

🔁 Step 3 – Two-Way Communication​

Currently, only the client talks and the server listens. We will now allow the server to respond.

3.1 Use threading to Listen and Write Simultaneously​

Create a file called server2.py and copy the following code:

import socket
import threading

def receive(connection):
while True:
try:
message = connection.recv(1024).decode()
if not message or message.lower() == "exit":
print("[Client] has left the conversation.")
print("[Server >>>] ", end="", flush=True)
break
print(f"[Client] {message}")
print("[Server >>>] ", end="", flush=True)
except: # noqa: E722
break

def send(connection):
while True:
try:
message = input("[Server >>>] ")
connection.sendall(message.encode())
if message.lower() == "exit":
break
except: # noqa: E722
break

# Create the server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 12345))
server.listen(1)

print("[Server] Waiting for connection...")
connection, address = server.accept()
print(f"[Server] Connected to {address}")

# Start the threads
thread1 = threading.Thread(target=receive, args=(connection,))
thread2 = threading.Thread(target=send, args=(connection,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
connection.close()
server.close()

Create a file called client2.py and copy the following code:

import socket
import threading

def receive(sock):
while True:
message = sock.recv(1024).decode()
if message.lower() == "exit":
print("[Server] has left the conversation.")
print("[Client >>>] ", end="", flush=True)
break
print(f"[Server] {message}")
print("[Client >>>] ", end="", flush=True)

def send(sock):
while True:
message = input("[Client >>>] ")
sock.sendall(message.encode())
if message.lower() == "exit":
break

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 12345))
print("[Client] Connected to the server.")

# Start the threads
thread1 = threading.Thread(target=receive, args=(client,))
thread2 = threading.Thread(target=send, args=(client,))
thread1.start()
thread2.start()

thread1.join()
thread2.join()
client.close()

✅ Expected Result​

  1. Run server2.py in one terminal.
  2. Run client2.py in another.
  3. Try exchanging messages in both directions.
  4. Cleanly close with exit.

Example of client-server communication

📝 Step 4 – The Operating System​

The operating system plays a crucial role in managing network connections. It manages resources, ports, and ensures that data is sent and received correctly. The same goes for threads, which allow multiple tasks to be executed in parallel, like listening and sending messages at the same time.

Answer the following questions:

  • In the server and client code, when is the operating system involved?
  • Are the threads managed by the operating system (OS thread) or by Python (application thread)?
  • Why is it important to close connections properly?
  • What happens if you try to run the server multiple times on the same port without closing it?
  • What happens if you try to run the client before the server?