Сетевые взаимодействия в Python: основные подходы

Раздел: Прочее -> Сетевые взаимодействия

Основные способы передачи данных в сети

Базовый подход: TCP сокеты

Как передать данные с гарантией доставки и сохранением порядка?

Протокол TCP обеспечивает надёжное соединение. Для передачи данных используется модуль socket. Создаётся сервер, который слушает порт, и клиент, который подключается. Пример простого эхо-сервера:


import socket

# Сервер
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind(('localhost', 9090))
server_sock.listen(1)
conn, addr = server_sock.accept()
with conn:
    print('Подключён клиент', addr)
    data = conn.recv(1024)
    print('Получено:', data.decode())
    conn.sendall(data)  # отправляем обратно

обмен данными python (обмен данными в python)

Клиент:


import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 9090))
sock.sendall(b'Hello, server!')
response = sock.recv(1024)
print('Ответ сервера:', response.decode())
sock.close()

передача данных python (передача данных в python)

Пояснения:

  • socket.socket() создаёт сокет с семейством адресов AF_INET (IPv4) и типом SOCK_STREAM (TCP).
  • bind() привязывает сокет к адресу и порту.
  • listen() переводит сокет в режим ожидания подключений.
  • accept() принимает входящее соединение, возвращает новый сокет и адрес клиента.
  • Данные передаются в байтах; для строк используется кодировка (обычно UTF-8).

Типичные проблемы:

  • Неверный порядок байтов (big-endian vs little-endian) при передаче чисел – решается использованием struct.pack/struct.unpack.
  • Блокирующие вызовы recv и accept без таймаута могут повесить программу. Устанавливайте таймаут через settimeout() или используйте неблокирующие режимы.
  • Потеря соединения вызывает исключение ConnectionResetError или BrokenPipeError – необходимо обрабатывать в try-except.

UDP сокеты

Как передать данные с минимальной задержкой, если потеря пакетов допустима?

UDP не гарантирует доставку, но работает быстрее TCP. Пример клиента и сервера:


# Сервер
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', 9091))
data, addr = sock.recvfrom(1024)
print('Получено от', addr, data.decode())
sock.sendto(b'Answer', addr)

# Клиент
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b'Hello', ('localhost', 9091))
response, server = sock.recvfrom(1024)
print('Ответ:', response.decode())

Проблемы: пакеты могут теряться, дублироваться, приходить не по порядку. Для критичных данных поверх UDP строят надёжные протоколы (например, QUIC).

Сериализация JSON

Как передать сложные структуры (словари, списки) по сети?

Преобразование данных в строку JSON и обратно. Пример с использованием TCP:


import json, socket

data = {'name': 'Alice', 'scores': [85, 92, 78]}
payload = json.dumps(data).encode()

sock = socket.socket()
sock.connect(('localhost', 9092))
sock.sendall(payload)
response = sock.recv(4096)
received = json.loads(response.decode())
print(received)  # обратно декодированный словарь

Ошибки: несоответствие кодировок, слишком большие сообщения (нужно фрагментировать), передача бинарных полей (JSON их не поддерживает). Для бинарных данных используйте base64 или Protocol Buffers.

HTTP через библиотеку requests

Как отправить и получить данные через веб-сервер?

Библиотека requests упрощает работу с HTTP. Пример отправки POST с JSON:


import requests

url = 'https://httpbin.org/post'
payload = {'key': 'value'}
response = requests.post(url, json=payload)
print(response.status_code)
print(response.json())

Для собственного сервера можно использовать Flask или http.server.

Типичные ошибки: игнорирование таймаутов (добавляйте параметр timeout), проблемы с SSL сертификатами (указывайте verify=False только для тестов), обработка статусов ошибок.

WebSocket

Как организовать постоянное двустороннее соединение?

WebSocket позволяет обмениваться сообщениями в реальном времени. Пример с библиотекой websockets (установка: pip install websockets):


# Сервер
import asyncio, websockets

async def handler(websocket):
    async for message in websocket:
        print('Получено:', message)
        await websocket.send('Эхо: ' + message)

async def main():
    async with websockets.serve(handler, 'localhost', 8765):
        await asyncio.Future()

asyncio.run(main())

# Клиент
import asyncio, websockets

async def client():
    async with websockets.connect('ws://localhost:8765') as websocket:
        await websocket.send('Привет!')
        response = await websocket.recv()
        print('Ответ:', response)

asyncio.run(client())

Проблемы: закрытие соединения (обработка исключения websockets.exceptions.ConnectionClosed), ограничение размера сообщения (по умолчанию 1 МБ).

Расширенные примеры передачи данных

Асинхронный TCP сервер на asyncio

Для обработки множества клиентов без потоков используется asyncio. Пример эхо-сервера:

Пример

import asyncio

async def handle_client(reader, writer):
    data = await reader.read(100)
    message = data.decode()
    addr = writer.get_extra_info('peername')
    print(f'Получено от {addr}: {message}')
    writer.write(data)
    await writer.drain()
    writer.close()

async def main():
    server = await asyncio.start_server(handle_client, 'localhost', 9090)
    print('Сервер запущен')
    async with server:
        await server.serve_forever()

asyncio.run(main())
Результат (при подключении клиента):
Получено от ('127.0.0.1', 54321): Hello

Клиент для асинхронного сервера можно написать также с asyncio, используя asyncio.open_connection.

Передача больших файлов с разбиением на чанки

Для передачи файлов размером > 1 МБ данные отправляют частями. Пример сервера, принимающего файл и сохраняющего его:

Пример

# Сервер
import socket

HOST = 'localhost'
PORT = 9090
CHUNK_SIZE = 4096

sock = socket.socket()
sock.bind((HOST, PORT))
sock.listen(1)
print('Сервер ожидает файл...')
conn, addr = sock.accept()
with conn:
    with open('received_file.bin', 'wb') as f:
        while True:
            chunk = conn.recv(CHUNK_SIZE)
            if not chunk:
                break
            f.write(chunk)
print('Файл сохранён')
Пример

# Клиент
import socket, os

HOST = 'localhost'
PORT = 9090
CHUNK_SIZE = 4096

with open('large_file.bin', 'rb') as f:
    sock = socket.socket()
    sock.connect((HOST, PORT))
    while True:
        chunk = f.read(CHUNK_SIZE)
        if not chunk:
            break
        sock.sendall(chunk)
    sock.close()
print('Файл отправлен')

Проблемы: отсутствие информации о размере файла (сервер не знает, когда закончится передача). Решение – сначала отправлять размер, затем данные. Также возможны ошибки при обрыве соединения.

Шифрование данных с помощью SSL

Для безопасной передачи данных поверх TCP используется SSL/TLS. Пример клиента и сервера с самоподписанным сертификатом:

Пример

# Генерация сертификата (в командной строке):
# openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

# Сервер
import socket, ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')

bindsocket = socket.socket()
bindsocket.bind(('localhost', 9090))
bindsocket.listen(5)

while True:
    newsocket, fromaddr = bindsocket.accept()
    conn = context.wrap_socket(newsocket, server_side=True)
    try:
        data = conn.recv(1024)
        print('Получено:', data.decode())
        conn.send(b'Hello, secure world!')
    finally:
        conn.shutdown(socket.SHUT_RDWR)
        conn.close()
Пример

# Клиент
import socket, ssl

context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE  # для самоподписанного сертификата

sock = socket.socket()
conn = context.wrap_socket(sock, server_hostname='localhost')
conn.connect(('localhost', 9090))
conn.send(b'Secret message')
response = conn.recv(1024)
print('Ответ:', response.decode())
conn.close()

Ошибка: несоответствие версий протоколов, проблемы с сертификатами – нужно указывать корректные сертификаты или отключать проверку (только для разработки).

Передача объектов через pickle

Модуль pickle позволяет сериализовать произвольные объекты Python. Пример:

Пример

import socket, pickle

class MyData:
    def __init__(self, name, value):
        self.name = name
        self.value = value

obj = MyData('test', 42)
data = pickle.dumps(obj)

sock = socket.socket()
sock.connect(('localhost', 9090))
sock.sendall(data)

На стороне сервера объект восстанавливается через pickle.loads.

Важно: pickle небезопасен при приёме данных от ненадёжных источников – может привести к выполнению произвольного кода. Используйте только для доверенных каналов. Альтернативы: JSON, msgpack, protobuf.

Многопоточный сервер для множества клиентов

Каждое соединение обрабатывается в отдельном потоке. Пример:

Пример

import socket, threading

def handle_client(conn):
    with conn:
        data = conn.recv(1024)
        print(f'Обработано: {data.decode()}')
        conn.sendall(data)
    print('Соединение закрыто')

sock = socket.socket()
sock.bind(('localhost', 9090))
sock.listen(5)
print('Сервер слушает...')
while True:
    conn, addr = sock.accept()
    threading.Thread(target=handle_client, args=(conn,)).start()

Клиент может подключаться несколько раз одновременно.

Проблемы: GIL (Global Interpreter Lock) не даёт выигрыша по CPU, но для I/O операций потоки подходят. Для высокой нагрузки лучше использовать asyncio или процессы.

Передача данных в Python - comments

En
передача данных python (python)