Socket: примеры (PYTHON)
socket(family, type, proto): socket.socketОсновы функции socket
Функция socket.socket() из одноименного модуля является конструктором для создания новых объектов сокетов в Python. Она служит основным инструментом для низкоуровневого сетевого взаимодействия, позволяя реализовывать клиент-серверную архитектуру, протоколы TCP/IP, UDP и другие.
Функция используется, когда требуется прямое управление сетевым соединением: создание серверов, клиентов, обмен данными между процессами на разных машинах или реализация специализированных сетевых протоколов.
Синтаксис вызова функции: socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None).
- family (семейство адресов): определяет пространство адресов. Основные значения:
socket.AF_INET(IPv4),socket.AF_INET6(IPv6),socket.AF_UNIX(Unix-доменные сокеты для межпроцессного взаимодействия на одной машине). - type (тип сокета): определяет тип коммуникации.
socket.SOCK_STREAM(потоковый сокет, TCP, обеспечивает надежное соединение),socket.SOCK_DGRAM(дейтаграммный сокет, UDP, без установки соединения),socket.SOCK_RAW(сырой сокет для низкоуровневых операций). - proto (протокол): номер протокола. Обычно равен 0, что означает выбор протокола по умолчанию для заданного семейства и типа (например, TCP для SOCK_STREAM).
- fileno: необязательный файловый дескриптор существующего сокета. Если указан, остальные параметры игнорируются, а функция возвращает сокет, связанный с этим дескриптором.
Возвращаемое значение: объект сокета, который поддерживает методы для привязки (bind), прослушивания (listen), подключения (connect), отправки (send) и получения (recv) данных.
Простые примеры использования
Создание TCP-сокета для IPv4 (наиболее распространенный вариант):
import socket
# Создание TCP/IP сокета
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f'Сокет создан: {sock}')
# Закрытие сокета
sock.close()<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
Создание UDP-сокета для IPv6:
import socket
# Создание UDP/IP сокета для IPv6
sock_udp = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
print(f'UDP IPv6 сокет: {sock_udp}')
sock_udp.close()<socket.socket fd=3, family=AddressFamily.AF_INET6, type=SocketKind.SOCK_DGRAM, proto=0>
Создание Unix-доменного потокового сокета:
import socket
sock_unix = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
print(f'Unix-сокет: {sock_unix}')
sock_unix.close()<socket.socket fd=3, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>
Похожие возможности в Python
Модуль socket предлагает дополнительные функции для работы с сокетами:
- socket.socketpair(): создает пару связанных сокетов, полезную для межпроцессного взаимодействия (IPC) в рамках одного хоста.
- socket.create_connection(address): высокоуровневая функция для установки TCP-соединения с указанным адресом, автоматически разрешающая доменные имена и обрабатывающая таймауты.
- socket.create_server(address) (Python 3.8+): удобная функция для создания TCP-сервера с привязкой к адресу и переходом в режим прослушивания.
- Модули высшего уровня: Для типичных задач часто предпочтительнее использовать библиотеки, построенные на сокетах:
http.serverдля HTTP,socketserverдля упрощения создания сетевых серверов,asyncioдля асинхронного программирования, фреймворки типаFlaskилиDjangoдля веб-разработки.
Функцию socket.socket() выбирают, когда необходим полный контроль над сетевым взаимодействием или при реализации нестандартных протоколов.
Аналоги функции в других языках
PHP: Функция socket_create имеет схожую семантику.
// Создание TCP сокета
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
var_dump($socket);resource(4) of type (Socket)
JavaScript (Node.js): Модуль net для TCP и dgram для UDP.
const net = require('net');
// Создание TCP сокета
const socket = new net.Socket();
console.log(socket);Socket { ... }Java: Класс java.net.Socket для клиента и java.net.ServerSocket для сервера.
import java.net.Socket;
Socket socket = new Socket();
System.out.println(socket);Socket[unconnected]
C#: Класс System.Net.Sockets.Socket.
using System.Net.Sockets;
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine(socket);System.Net.Sockets.Socket
Golang: Пакет net, функции net.Dial для клиента и net.Listen для сервера.
package main
import "net"
conn, _ := net.Dial("tcp", "golang.org:80")
println(conn)&{0xc00011a000}Отличия от Python часто заключаются в объектно-ориентированном дизайне (Java, C#) или более высокой уровне абстракции (Golang, Node.js).
Распространенные ошибки
Address already in use: Попытка привязать сокет к порту, который уже занят другим процессом.
import socket
import time
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind(('127.0.0.1', 8080))
s1.listen(1)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s2.bind(('127.0.0.1', 8080))
except OSError as e:
print(f'Ошибка: {e}')
s1.close()
s2.close()Ошибка: [Errno 98] Address already in use
Connection refused: Клиент пытается подключиться к порту, на котором нет слушающего сервера.
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.connect(('127.0.0.1', 9999)) # Предполагаем, что порт свободен
except ConnectionRefusedError as e:
print(f'Ошибка подключения: {e}')
client.close()Ошибка подключения: [Errno 111] Connection refused
Blocking operation timed out: Операция (например, recv) превысила установленный таймаут.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5) # Таймаут 0.5 секунды
try:
# Попытка принять данные, которых нет
data = s.recv(1024)
except socket.timeout:
print('Истекло время ожидания данных')
s.close()Истекло время ожидания данных
Изменения в последних версиях Python
- Python 3.8: Добавлена константа
socket.AF_CANдля работы с Controller Area Network (CAN). Добавлен флагsocket.SOCK_NONBLOCKдля Linux/BSD, упрощающий создание неблокирующих сокетов при использовании сtype(например,socket.SOCK_STREAM | socket.SOCK_NONBLOCK). - Python 3.9: Добавлены константы для адресов IPv4-mapped IPv6 (
socket.IPPROTO_IPV6,socket.IPV6_V6ONLY). - Python 3.10: Улучшена поддержка перечисления
IntEnumдля семейств адресов и типов сокетов, что упрощает их отладку и использование. - Python 3.11: Внесены внутренние оптимизации, влияющие на производительность операций с сокетами. Добавлена функция
socket.sethostname()(доступна только на Unix, требует привилегий).
Общее направление изменений - добавление поддержки новых протоколов и системных вызовов, а также улучшение удобства использования и производительности.
Расширенные примеры применения
Создание неблокирующего сокета (Linux/BSD):
import socket
import sys
if sys.platform.startswith('linux'):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM | socket.SOCK_NONBLOCK)
print('Неблокирующий сокет создан:', sock.getblocking())
else:
print('Платформа не поддерживает SOCK_NONBLOCK')
# Альтернативный способ на всех платформах
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock2.setblocking(False)
print('Сокет setblocking(False):', sock2.getblocking())Неблокирующий сокет создан: False Сокет setblocking(False): False
Использование сырого сокета для отправки кастомного ICMP-пакета (требует прав администратора/root):
import socket
import struct
import os
# Только для демонстрации структуры
if os.name == 'posix' and os.geteuid() == 0:
try:
# Создание сырого сокета для ICMP (протокол 1)
sock_raw = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
# Создание простого ICMP echo-запроса (тип 8, код 0)
header = struct.pack('bbHHh', 8, 0, 0, 1, 1)
sock_raw.sendto(header, ('8.8.8.8', 0))
print('Сырой ICMP-пакет отправлен')
except PermissionError:
print('Недостаточно прав для создания сырого сокета')
finally:
if 'sock_raw' in locals():
sock_raw.close()
else:
print('Пример требует прав администратора (root) на Unix-системах')Пример требует прав администратора (root) на Unix-системах
Создание сокета из существующего файлового дескриптора (например, переданного от родительского процесса):
import socket
import os
# Создаем обычный сокет и получаем его файловый дескриптор
sock_orig = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd = sock_orig.fileno()
print('Исходный файловый дескриптор:', fd)
# Создаем новый объект сокета из дескриптора
sock_from_fd = socket.socket(fileno=fd)
print('Сокет из дескриптора:', sock_from_fd)
# Закрываем один из объектов - оба ссылаются на один ресурс
sock_orig.close()
print('Сокет из fd всё еще открыт?', not sock_from_fd._closed)
sock_from_fd.close()Исходный файловый дескриптор: 3 Сокет из дескриптора: <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> Сокет из fd всё еще открыт? True