Creating a Python TCP Server Model With Socket Library

A server is a computer program or device that provides functionality for other programs or devices, known as clients. Servers typically handle various tasks such as storing and retrieving files, managing network resources, providing access to databases, hosting websites, or delivering services over a network, such as email or online gaming.

In simpler terms, you can think of a server as a central hub that fulfills requests from other devices or programs, allowing them to access resources or services provided by the server. Servers can range from basic home servers used for personal file storage to powerful data center servers that support large-scale applications and services for millions of users.

Oops, our bad! Let’s rewind a bit. Hello! How’s it going? Today, in this tutorial, we’re jumping straight into creating a TCP-based server with Python’s Socket library. No time to waste, let’s dive right in!

Getting Started

To craft our Python TCP Server, we’ll be harnessing the power of a networking gem known as the Socket library. Refer to the Socket Library Documentation for more details.

The Python Socket library provides a set of modules that allows you to easily create and interact with sockets, which are endpoints for communication between two machines over a network. It enables you to develop networking applications such as creating servers and clients, transferring data between systems, and implementing various network protocols like TCP (Transmission Control Protocol) and UDP (User Datagram Protocol). With the Socket library, you can handle low-level network communication efficiently and effectively in Python.

I understand you’re eager to jump into coding the server. Alright, to kick things off, let’s create a file named server.py.

Now that we’ve got our Python file set up, let’s begin by importing the necessary Python libraries that will assist us in crafting the TCP server.

import socket
import concurrent.futures
from colorama import Fore, Style

Here’s what each imported library does:

  • socket: This library provides low-level networking interfaces for creating and interacting with sockets, which are endpoints for communication over a network. It is used here to create and manage the server socket, accept incoming connections, and send/receive data.
  • concurrent.futures: This library provides a high-level interface for asynchronously executing callables. It’s utilized here to handle multiple client connections concurrently using a ThreadPoolExecutor. This allows the server to handle multiple clients simultaneously without blocking on any single client.
  • colorama: This library provides cross-platform support for colored terminal text and styling. It’s used here to print colored output to the terminal, making it easier to distinguish different messages and statuses printed during the server’s operation.

We’ve completed importing all necessary libraries. Next up, let’s establish the server class. This class will house methods to initialize the server, kickstart connection listening, and manage client connections seamlessly. As the server springs to action, it’ll eagerly await connections on the designated host and port. Upon a client’s arrival, a dedicated thread leaps into existence, swiftly taking charge of communication with the client. This ensures our server remains open and available, efficiently catering to multiple client connections concurrently.

class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print(f"{Fore.LIGHTBLUE_EX}[*] {Style.RESET_ALL}Server Started")
        

    def start(self):
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(5)  # Listen for incoming connections
        print(f"{Fore.LIGHTBLUE_EX}[*] {Style.RESET_ALL}Server listening on {Fore.LIGHTGREEN_EX}{self.host}:{self.port}{Style.RESET_ALL}")

        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            while True:
                client_socket, client_address = self.server_socket.accept()
                print(f"Connection from {client_address}")
                executor.submit(self.handle_client, client_socket)

    def handle_client(self, client_socket):
        try:
            with client_socket:
                while True:
                    data = client_socket.recv(1024)
                    if not data:
                        break
                    message = data.decode("utf-8")
                    print(f"Received message: {message}")
                    # Process the received message
                    # Example: echo back the message
                    client_socket.sendall(data)
        except Exception as e:
            print(f"Error: {e}")
        print(f"Client disconnected")

Now, let’s break down the above code step by step:

Class Definition:

  • class Server:: This line defines a class named Server. This class represents our TCP server.

Constructor Method (__init__):

  • __init__ is a special method called the constructor, which initializes a new instance of the Server class.
  • It takes host and port parameters to specify the server’s address and port.
  • Inside the constructor:
    • self.host = host: Sets the host attribute of the instance to the provided host.
    • self.port = port: Sets the port attribute of the instance to the provided port.
    • self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM): Creates a new socket object for the server using IPv4 addressing (AF_INET) and TCP protocol (SOCK_STREAM).
    • print(f"{Fore.LIGHTBLUE_EX}[*] {Style.RESET_ALL}Server Started"): Prints a message indicating that the server has started. The {Fore.LIGHTBLUE_EX} and {Style.RESET_ALL} are color codes from the colorama library for styling output.

start Method:

  • This method is responsible for starting the server and handling incoming client connections.
  • It first binds the server socket to the specified host and port.
  • Then, it starts listening for incoming connections with the listen method, allowing a maximum of 5 pending connections in the backlog.
  • It prints a message indicating that the server is listening on the specified address and port.
  • It uses a ThreadPoolExecutor from the concurrent.futures module to handle incoming connections concurrently. It creates a new thread for each client connection.
  • Inside the loop, it accepts incoming client connections using the accept method of the server socket. When a new connection is established, it prints a message indicating the connection’s origin and submits a task to the thread pool to handle the client.

handle_client Method:

  • This method is responsible for handling communication with a single client.
  • It continuously receives data from the client using the recv method of the client socket.
  • If the received data is not empty, it decodes it into a UTF-8 encoded string and prints it.
  • It then sends the received data back to the client using the sendall method of the client socket.
  • If an exception occurs during communication with the client, it prints an error message.
  • Finally, it prints a message indicating that the client has disconnected.

These methods collectively define the functionality of the TCP server, from initialization to handling client connections and communication.

Having crafted the server class, our next objective is to forge a main function, serving as the gateway for executing our TCP server script.

if __name__ == "__main__":
    host = "192.168.1.3"
    port = 5555
    server = Server(host, port)
    try:
        server.start()
    except KeyboardInterrupt:
        print("\nServer stopped.")
    finally:
        server.server_socket.close()

This code segment serves as the main entry point for executing the TCP server script. It begins by defining the host IP address and port number on which the server will listen for incoming connections. Following this, an instance of the Server class is created, initialized with the specified host and port details. The server’s execution is initiated within a try block, calling the start method of the server object to begin listening for incoming connections and handling client requests. In the event of a keyboard interrupt, denoted by the KeyboardInterrupt exception.

the script gracefully handles the interruption by printing a message indicating the server’s termination. Finally, in a finally block, the server socket is closed to ensure proper cleanup and release of resources, regardless of whether an exception occurred during server execution. This ensures the smooth and reliable operation of the TCP server, from initialization to termination, providing robust network communication capabilities.

NB: Remember to replace ‘host‘ with your local machine’s IP address before running the script.

Boom! We’ve nailed it! All the code blocks are in place, paving the way for a TCP server extravaganza with sockets!

Hey, hey, I see you’re all about making life easier! No sweat, I’ll gather up those code blocks and whip them into one tidy file for you. Consider it a copy-paste convenience upgrade!

import socket
import concurrent.futures
from colorama import Fore, Style

class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print(f"{Fore.LIGHTBLUE_EX}[*] {Style.RESET_ALL}Server Started")
        

    def start(self):
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(5)  # Listen for incoming connections
        print(f"{Fore.LIGHTBLUE_EX}[*] {Style.RESET_ALL}Server listening on {Fore.LIGHTGREEN_EX}{self.host}:{self.port}{Style.RESET_ALL}")

        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            while True:
                client_socket, client_address = self.server_socket.accept()
                print(f"Connection from {client_address}")
                executor.submit(self.handle_client, client_socket)

    def handle_client(self, client_socket):
        try:
            with client_socket:
                while True:
                    data = client_socket.recv(1024)
                    if not data:
                        break
                    message = data.decode("utf-8")
                    print(f"Received message: {message}")
                    # Process the received message
                    # Example: echo back the message
                    client_socket.sendall(data)
        except Exception as e:
            print(f"Error: {e}")
        print(f"Client disconnected")

if __name__ == "__main__":
    host = "192.168.1.3"
    port = 5555
    server = Server(host, port)
    try:
        server.start()
    except KeyboardInterrupt:
        print("\nServer stopped.")
    finally:
        server.server_socket.close()

Code Execution and Output Display

As we conclude our tutorial, it’s time to put our Python script into action and observe the output. To execute the Python script, navigate to your working directory in the terminal and enter the following command:

Bash
python server.py

After executing the Python script, behold the output showcased in the image below.

python tcp server

Leave a Reply

Your email address will not be published. Required fields are marked *

DarkLight
×