Socket Programming in Python

Pratik Choudhari

Web Sockets allow a bi-directional channels between client and server whereas REST APIs do not offer a full-duplex connection. Python has built-in support for creating server sockets and connecting to them as a client.

In this blog, we will understand Pythonโ€™s sockets module with a few examples of how to create a web socket server and connect using a client.

Understanding web sockets

Web socket is a communication protocol to facilitate bi-directional and full-duplex channels for sharing messages. It works in a client-server system. Unlike HTTP, the connections in web sockets are established once, persisted, and need to be closed formally.

Sockets are of multiple types like TCP and UNIX. They also have a separate URL scheme. For a REST API a URL looks like http://www.python-engineer.com/blogs/create whereas for a web socket it looks like ws://www.python-engineer.com/blogs/comments?id=471

Areas of applications for web sockets include real-time updates and push-based events.

Next, we will be looking at how to create a socket server in Python.

Creating a socket server

The way a socket server works is very different compared to a REST API server. As socket TCP connections are persistent, each client connection is kept alive until it is explicitly closed.

The following steps are performed on a socket server:

Example of a web socket server:

import socket SERVER = socket.gethostbyname(socket.gethostname()) PORT = 9797 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((SERVER, PORT)) s.listen(5) print(f"[INFO] Listening on {SERVER}:{PORT}") while True: conn, addr = s.accept() with conn: print(f"[CONNECTION] Connected to {addr}") while True: data = conn.recv(1024) if not data: break conn.sendall(f"[CONNECTION] Received {data} from {addr}".encode()) print(f"[CONNECTION] Disconnected from {addr}")

The verbs bind, listen, accept, send, recv, and close are most used in socket programming.

The sockets object is created using the socket() method. This socket has to be bound to a host and port. We achieve this using bind().

The listen() method asks the socket server to look out for pending connection requests. It takes a number as a parameter which defines the number of unaccepted connections allowed before declining an incoming request.

Once a connection request is received it is accepted using the accept() method. It is important to note that the server has to run indefinitely to keep listening for new connections and accept them.

accept() returns two objects, the first is a socket connection object and the second is the address of the client. Any further communication with a client is performed using this connection object returned by accept.

Next, the server waits for a message from the client using the recv() method which takes the max number of bytes to receive from the client at a time and returns the data sent by the client as bytes.

As one can see, the server waits for new connections indefinitely without any timeout also as long as a connection is alive. The server cannot accept new connections because every connection is blocking.

Simultanous socket connections

We will improve the server from above using multi-threading to work with multiple connections simultaneously:

import socket import threading SERVER = socket.gethostbyname(socket.gethostname()) PORT = 9797 def client_thread(conn, addr): with conn: print(f"[CONNECTION] Connected to {addr}") while True: data = conn.recv(1024) if not data: break conn.sendall(f"[CONNECTION] Received {data} from {addr}".encode()) print(f"[CONNECTION] Disconnected from {addr}") with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((SERVER, PORT)) s.listen(5) print(f"[INFO] Listening on {SERVER}:{PORT}") while True: conn, addr = s.accept() print(f"[INFO] Starting thread for connection {addr}") thread = threading.Thread(target=client_thread, args=(conn, addr)) thread.start()

Now with multi-threading, each client connected to the server is handled in its own thread.

Creating a socket client

Creating a socket client is simple, a client just connects to the server address and sends data as and when needed. Once all messages are exchanged the connection is closed.

Let's see an example:

import socket import time SERVER = "192.168.56.1" PORT = 9797 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((SERVER, PORT)) client.send("Hello world!".encode()) time.sleep(4) client.shutdown(1) client.close()

FREE VS Code / PyCharm Extensions I Use

โœ… Write cleaner code with Sourcery, instant refactoring suggestions: Link *

* This is an affiliate link. By clicking on it you will not have any additional costs, instead you will support me and my project. Thank you! ๐Ÿ™

Check out my Courses