How do I work with a Python socket?

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What are Sockets in Python and How Do They Work

A socket is a software interface that provides two-way communication between nodes in a computer network. It is a virtual connector through which applications can send and receive data over the network.

Modern network applications actively use Python sockets to create various systems. From messengers to online games and data exchange services, network interaction via sockets is used everywhere.

Basic Concepts of Network Programming

To work with sockets in Python, you need to understand the key concepts:

  • IP address — a unique address of a device on the network
  • Port — a numerical identifier of a service on the device
  • TCP (Transmission Control Protocol) — a reliable data transmission protocol
  • UDP (User Datagram Protocol) — a fast protocol without delivery confirmation

TCP provides guaranteed data delivery with error control. UDP works faster but does not guarantee message delivery.

Setting Up and Importing the socket Library

The socket module is included in the standard Python library. To start working with sockets, it is enough to perform a simple import:

import socket

No additional installations are required. The socket library is available in all versions of Python by default.

Creating a TCP Server in Python

Basic Server Implementation

A TCP server accepts connections from clients and processes their requests. Consider a simple implementation:

import socket

# Creating a TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Binding to an address and port
server_socket.bind(('localhost', 12345))

# Starting listening (maximum 5 connections in the queue)
server_socket.listen(5)

print("Server started. Waiting for connections...")

while True:
    # Accepting a connection
    client_socket, address = server_socket.accept()
    print(f"Connection from {address}")

    # Receiving a message
    message = client_socket.recv(1024).decode('utf-8')
    print(f"Received message: {message}")

    # Sending a response
    client_socket.send("Hello from the server!".encode('utf-8'))

    # Closing the connection with the client
    client_socket.close()

Understanding the Server Operation Process

The following operations occur in the given code:

  1. A socket is created using the TCP protocol.
  2. The server is bound to the address localhost and port 12345.
  3. Waiting for client connections is started.
  4. When a client connects, its message is accepted.
  5. The server sends a response and closes the connection.

The socket.AF_INET method specifies the use of IPv4, and socket.SOCK_STREAM specifies the TCP protocol.

Developing a TCP Client

A TCP client connects to the server and exchanges data with it. Example of a client implementation:

import socket

# Creating a client socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connecting to the server
client_socket.connect(('localhost', 12345))

# Sending a message
client_socket.send("Hello, server!".encode('utf-8'))

# Receiving a response
response = client_socket.recv(1024).decode('utf-8')
print(f"Response from server: {response}")

# Closing the connection
client_socket.close()

The client performs a sequence of actions: connection, sending data, receiving a response, and closing the connection.

Working with the UDP Protocol

UDP provides a faster way to transmit data without delivery guarantees. This makes it suitable for real-time applications.

UDP Server

import socket

# Creating a UDP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Binding to an address
server_socket.bind(('localhost', 54321))

print("UDP server started...")

while True:
    # Receiving data and sender address
    data, addr = server_socket.recvfrom(1024)
    print(f"Received message from {addr}: {data.decode('utf-8')}")

    # Sending a response
    server_socket.sendto("Response from server".encode('utf-8'), addr)

UDP Client

import socket

# Creating a UDP client
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Sending data
client_socket.sendto("Hello, UDP server!".encode('utf-8'), ('localhost', 54321))

# Receiving a response
data, addr = client_socket.recvfrom(1024)
print(f"Server response: {data.decode('utf-8')}")

client_socket.close()

Differences Between UDP and TCP

UDP uses the sendto() and recvfrom() methods instead of send() and recv(). This is because UDP does not establish a permanent connection between the client and the server.

Error Handling When Working with Sockets

Network programming is associated with various types of errors. Correct exception handling is critical for the stable operation of applications.

Typical Socket Errors

The main problems that can arise:

  • The server is unavailable or not running
  • The network connection is suddenly broken
  • The connection timeout has been exceeded
  • The port is already in use by another application
  • Insufficient rights to bind to the port

Example of Error Handling

import socket

try:
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Setting a timeout of 5 seconds
    client_socket.settimeout(5)

    # Attempting to connect
    client_socket.connect(('localhost', 12345))

    # Sending a message
    client_socket.send("Message to server".encode('utf-8'))

    # Receiving a response
    response = client_socket.recv(1024).decode('utf-8')
    print(f"Response received: {response}")

except socket.timeout:
    print("Error: Connection timeout expired")

except ConnectionRefusedError:
    print("Error: Server unavailable or refused connection")

except socket.gaierror:
    print("Error: Invalid address or hostname")

except Exception as e:
    print(f"Unexpected error: {e}")

finally:
    # Guaranteed socket closure
    try:
        client_socket.close()
    except:
        pass

Security Measures for Sockets

The security of network applications requires a comprehensive approach and adherence to various recommendations.

Basic Security Principles

  • Input validation — always check data from clients
  • Resource limitation — control the number of connections and the size of messages
  • Using encryption — use SSL/TLS to protect data
  • Authentication — verify client access rights
  • Logging — keep a log of connections and operations

Implementing SSL Encryption

import socket
import ssl

# Creating an SSL context
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

# Loading the certificate and key
context.load_cert_chain('server.crt', 'server.key')

# Creating a server with SSL
with socket.create_server(('localhost', 12345)) as server_socket:
    with context.wrap_socket(server_socket, server_side=True) as secure_socket:
        conn, addr = secure_socket.accept()
        print(f"Secure connection from {addr}")

        # Processing data over an encrypted connection
        data = conn.recv(1024)
        conn.send(b"Secure response")

Resource Limitation

import socket
import threading
import time

MAX_CONNECTIONS = 10
active_connections = 0
connection_lock = threading.Lock()

def handle_client(client_socket, address):
    global active_connections

    try:
        # Limiting the size of received data
        data = client_socket.recv(1024)  # Maximum 1KB

        if len(data) > 1024:
            client_socket.send(b"Error: Message too long")
            return

        # Processing data
        response = f"Processed: {data.decode('utf-8')[:100]}"  # Limiting response length
        client_socket.send(response.encode('utf-8'))

    except Exception as e:
        print(f"Error processing client {address}: {e}")
    finally:
        client_socket.close()
        with connection_lock:
            active_connections -= 1

# Server with connection limitation
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 12345))
server_socket.listen(5)

while True:
    client_socket, address = server_socket.accept()

    with connection_lock:
        if active_connections >= MAX_CONNECTIONS:
            client_socket.send(b"Server busy. Try again later.")
            client_socket.close()
            continue
        active_connections += 1

    # Starting processing in a separate thread
    client_thread = threading.Thread(target=handle_client, args=(client_socket, address))
    client_thread.start()

Socket Module Function Reference

Creating and Configuring Sockets

  • socket.socket() — creates a new socket
  • socket.bind() — binds a socket to an address and port
  • socket.listen() — puts the socket into listening mode
  • socket.accept() — accepts an incoming connection
  • socket.connect() — connects to a remote host

Data Transfer

  • socket.send() — sends data over TCP
  • socket.sendto() — sends data over UDP
  • socket.recv() — receives data over TCP
  • socket.recvfrom() — receives data over UDP
  • socket.sendall() — sends all data guaranteed

Connection Management

  • socket.close() — closes the socket
  • socket.settimeout() — sets the operation timeout
  • socket.shutdown() — partially closes the connection
  • socket.setsockopt() — configures socket parameters

Practical Examples of Use

Sending Files Via Sockets

import socket
import os

def send_file(filename, host, port):
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        client_socket.connect((host, port))

        # Getting the file size
        file_size = os.path.getsize(filename)

        # Sending the file name and size
        file_info = f"{os.path.basename(filename)}:{file_size}".encode('utf-8')
        client_socket.send(file_info)

        # Waiting for confirmation
        response = client_socket.recv(1024)
        if response == b"OK":
            # Sending the file content
            with open(filename, 'rb') as file:
                while True:
                    chunk = file.read(4096)  # Read in 4KB chunks
                    if not chunk:
                        break
                    client_socket.sendall(chunk)

            print("File sent successfully")

    except Exception as e:
        print(f"Error sending file: {e}")
    finally:
        client_socket.close()

# Usage
send_file("document.pdf", "localhost", 12345)

Multi-threaded Chat Server

import socket
import threading

class ChatServer:
    def __init__(self, host='localhost', port=12345):
        self.host = host
        self.port = port
        self.clients = []
        self.nicknames = []

    def broadcast(self, message):
        """Sending a message to all clients"""
        for client in self.clients:
            try:
                client.send(message)
            except:
                # Removing a disconnected client
                self.remove_client(client)

    def remove_client(self, client):
        """Removing a client from the lists"""
        if client in self.clients:
            index = self.clients.index(client)
            self.clients.remove(client)
            nickname = self.nicknames[index]
            self.nicknames.remove(nickname)
            self.broadcast(f"{nickname} left the chat!".encode('utf-8'))
            client.close()

    def handle_client(self, client):
        """Handling messages from the client"""
        while True:
            try:
                message = client.recv(1024)
                if message:
                    self.broadcast(message)
                else:
                    self.remove_client(client)
                    break
            except:
                self.remove_client(client)
                break

    def start_server(self):
        """Starting the server"""
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((self.host, self.port))
        server_socket.listen()

        print(f"Server started on {self.host}:{self.port}")

        while True:
            client, address = server_socket.accept()
            print(f"Client connected {address}")

            # Requesting a nickname
            client.send("NICK".encode('utf-8'))
            nickname = client.recv(1024).decode('utf-8')

            self.nicknames.append(nickname)
            self.clients.append(client)

            print(f"Client nickname: {nickname}")
            self.broadcast(f"{nickname} joined the chat!".encode('utf-8'))

            # Starting a thread to handle the client
            thread = threading.Thread(target=self.handle_client, args=(client,))
            thread.start()

# Starting the server
if __name__ == "__main__":
    chat_server = ChatServer()
    chat_server.start_server()

Frequently Asked Questions

How to Send a File Through a Socket?

To send files over sockets, read the file in blocks and transmit them sequentially:

with open('file.txt', 'rb') as f:
    while True:
        chunk = f.read(4096)
        if not chunk:
            break
        client_socket.sendall(chunk)

How to Create a Multi-threaded Server?

Use the threading module to handle each client in a separate thread:

import threading

def handle_client(client_socket):
    # Client processing
    pass

while True:
    client, addr = server_socket.accept()
    thread = threading.Thread(target=handle_client, args=(client,))
    thread.start()

What Ports to Use for Applications?

Recommendations for choosing ports:

  • Avoid ports 1-1023 (system services)
  • Use the range 1024-65535 for custom applications
  • Check port availability before use
  • Popular ranges: 3000-3999, 8000-8999

What to Do With a Busy Port?

Options for solving the problem of a busy port:

  • Terminate the application using the port
  • Choose another free port
  • Use the SO_REUSEADDR option for reuse
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

Can I Use Asynchronous Sockets?

Yes, Python supports asynchronous operation with sockets through the asyncio module:

import asyncio

async def handle_client(reader, writer):
    data = await reader.read(1024)
    writer.write(b"Response")
    await writer.drain()
    writer.close()

async def main():
    server = await asyncio.start_server(handle_client, 'localhost', 12345)
    await server.serve_forever()

asyncio.run(main())

Which Protocol to Choose: TCP or UDP?

The choice of protocol depends on the application requirements:

  • TCP is recommended when:
    • Data delivery reliability is important
    • Error control is required
    • The application works with critical information
    • Examples: web browsers, email, file transfer
  • UDP is suitable when:
    • Transmission speed is critical
    • Loss of individual packets is acceptable
    • The application works in real time
    • Examples: online games, video streaming, DNS queries

Conclusion

Working with sockets in Python opens up wide opportunities for creating network applications. The socket module provides all the necessary tools to implement both simple and complex client-server solutions.

Having mastered the basics of working with TCP and UDP protocols, proper error handling, and security measures, you will be able to create reliable network applications. Further skill development is possible through the study of asynchronous approaches using the asyncio and selectors libraries and specialized frameworks such as Twisted or Socket.IO.

The practical application of the knowledge gained will help deepen the understanding of network programming and create effective solutions for various tasks.

News