Accept: примеры (PYTHON)
accept: (socket, address)Основы функции accept в Python
Функция accept() является методом объектов сокетов в Python, предназначенным для принятия входящих подключений в сетевом программировании. Она применяется в серверной части TCP-соединений после вызова методов bind() и listen().
Метод accept() не принимает аргументов, но работает в блокирующем режиме по умолчанию, ожидая подключения клиента. Возвращает кортеж из двух элементов: новый объект сокета для взаимодействия с клиентом и адресную пару (хост, порт) клиента. Новый сокет используется для отправки и получения данных, в то время как оригинальный серверный сокет продолжает прослушивание порта.
Блокирующее поведение можно изменить, установив тайм-аут через settimeout() или переведя сокет в неблокирующий режим с помощью setblocking(False). В неблокирующем режиме при отсутствии подключений возникает ошибка BlockingIOError.
Примеры использования accept
Простой TCP-сервер с блокирующим accept:
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 9090))
server_socket.listen(1)
print("Ожидание подключения...")
client_socket, addr = server_socket.accept()
print(f"Подключен клиент с адресом: {addr}")
client_socket.send(b"Hello")
client_socket.close()
server_socket.close()Ожидание подключения...
Подключен клиент с адресом: ('127.0.0.1', 54322)Использование accept с тайм-аутом:
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 9091))
server_socket.listen(1)
server_socket.settimeout(5.0)
try:
client_socket, addr = server_socket.accept()
print(f"Подключение установлено: {addr}")
except socket.timeout:
print("Тайм-аут ожидания подключения")
finally:
server_socket.close()Тайм-аут ожидания подключения
Альтернативные функции в Python
select.select() и selectors позволяют мониторить несколько сокетов на готовность к операциям ввода-вывода, включая возможность приема соединений. Модуль selectors предоставляет высокоуровневый интерфейс для мультиплексирования.
asyncio.start_server() используется в асинхронном программировании для создания TCP-серверов. Функция принимает подключения асинхронно, не блокируя цикл событий.
socketserver модуль предоставляет классы для создания сетевых серверов, которые инкапсулируют логику обработки подключений. Классы TCPServer и ThreadingTCPServer управляют сокетами автоматически.
Выбор зависит от требований: socket.accept() подходит для низкоуровневого контроля, selectors для мультиплексирования, asyncio для асинхронных приложений, а socketserver для быстрого создания серверов с обработкой запросов.
Аналоги функции в других языках
PHP: socket_accept() принимает соединение на сокете, возвращая новый ресурс сокета или false при ошибке.
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '127.0.0.1', 9090);
socket_listen($server);
$client = socket_accept($server);
echo "Клиент подключен\n";
socket_close($client);
socket_close($server);Клиент подключен
JavaScript (Node.js): Серверные сокеты создаются через модуль net, метод listen() начинает прослушивание, а событие 'connection' обрабатывает подключения.
const net = require('net');
const server = net.createServer();
server.listen(9090, () => {
console.log('Сервер запущен');
});
server.on('connection', (socket) => {
console.log('Новое подключение');
socket.end();
});Сервер запущен Новое подключение
Java: ServerSocket.accept() возвращает объект Socket для общения с клиентом, блокируя поток до подключения.
ServerSocket server = new ServerSocket(9090);
Socket client = server.accept();
System.out.println("Клиент подключен");
client.close();
server.close();Клиент подключен
Go: Метод Accept() типа net.Listener возвращает соединение и ошибку, требует явного закрытия соединений.
ln, _ := net.Listen("tcp", ":9090")
conn, _ := ln.Accept()
fmt.Println("Подключение установлено")
conn.Close()
ln.Close()Подключение установлено
Типичные ошибки при работе с accept
Использование accept на непрослушиваемом сокете вызывает ошибку.
import socket
s = socket.socket()
s.bind(('localhost', 9092))
try:
client, addr = s.accept()
except OSError as e:
print(f"Ошибка: {e}")Ошибка: [WinError 10022]
Попытка вызова accept на закрытом сокете приводит к исключению.
import socket
server = socket.socket()
server.bind(('localhost', 9093))
server.listen(1)
server.close()
try:
client, addr = server.accept()
except OSError as e:
print(f"Ошибка: {e}")Ошибка: [WinError 10038]
Игнорирование адреса клиента может привести к проблемам при логировании или аутентификации.
client_socket, _ = server_socket.accept() # Потеря информации об адресеИзменения в последних версиях Python
Начиная с Python 3.5, сокеты поддерживают менеджеры контекста, что упрощает управление ресурсами.
with socket.socket() as server:
server.bind(('localhost', 9094))
server.listen(1)
client, addr = server.accept()
with client:
client.send(b"data")В Python 3.8 добавлены проверки для корректного использования семейства адресов и типа сокета, что делает некоторые ошибки более информативными. Функция accept() осталась неизменной в своей основе, но улучшена интеграция с механизмами асинхронного ввода-вывода.
Расширенные примеры применения
Многопоточный сервер с обработкой нескольких подключений:
import socket
import threading
def handle_client(client_socket, addr):
print(f"Обработка клиента {addr}")
client_socket.send(b"Данные")
client_socket.close()
server = socket.socket()
server.bind(('localhost', 9095))
server.listen(5)
print("Сервер запущен")
while True:
client, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(client, addr))
thread.start()Использование accept с селектором для обработки нескольких серверных сокетов:
import socket
import selectors
sel = selectors.DefaultSelector()
def accept_connection(server_socket, mask):
client, addr = server_socket.accept()
print(f"Принято от {addr}")
sel.register(client, selectors.EVENT_READ, read_data)
server1 = socket.socket()
server1.bind(('localhost', 9096))
server1.listen()
sel.register(server1, selectors.EVENT_READ, accept_connection)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)Сервер с ограничением по количеству подключений и логгированием:
import socket
import time
max_connections = 3
connection_count = 0
server = socket.socket()
server.bind(('localhost', 9097))
server.listen(max_connections)
print(f"Максимум подключений: {max_connections}")
while connection_count < max_connections:
client, addr = server.accept()
connection_count += 1
print(f"[{time.ctime()}] Подключение {connection_count} от {addr}")
client.send(f"Вы подключение №{connection_count}".encode())
client.close()
server.close()