Berkeley sockets¶
Berkeley sockets — это программный интерфейс (API) для взаимодействия между процессами по сети, разработанный в Калифорнийском университете в Беркли (США) в начале 1980-х годов. Он предоставляет абстракцию «сокета» — конечной точки сетевого соединения, через которую приложения могут отправлять и получать данные, используя различные транспортные протоколы, прежде всего TCP/IP. Berkeley sockets стали стандартом де-факто для сетевого программирования в операционных системах семейства Unix и легли в основу многих современных сетевых API, включая Windows Sockets (Winsock).
¶История
¶Разработка в BSD
Первая реализация сокетов была создана в рамках проекта Berkeley Software Distribution (BSD) — версии операционной системы Unix, разрабатываемой в Калифорнийском университете в Беркли. В 1982 году в выпуске 4.1cBSD появился прототип интерфейса, а в 1983 году в 4.2BSD он был представлен как полноценный API. Основными разработчиками выступили Билл Джой (впоследствии сооснователь Sun Microsystems) и Сэмюэл Леффлер. Целью было создание универсального интерфейса, который бы абстрагировал приложения от деталей сетевых протоколов и аппаратного обеспечения.
¶Распространение и стандартизация
С распространением BSD-систем (FreeBSD, NetBSD, OpenBSD) и их производных (например, SunOS, Ultrix) интерфейс сокетов стал общепринятым. В 1988 году он был включён в стандарт POSIX (IEEE 1003.1), что обеспечило переносимость кода между различными Unix-подобными системами. В 1990-х годах Microsoft реализовала собственный вариант — Winsock, который, хотя и имел отличия, был основан на тех же принципах и функциях, что и Berkeley sockets.
¶Архитектура и основные понятия
¶Сокет
Сокет — это абстрактный объект, представляющий конечную точку двустороннего канала связи. В операционной системе сокет идентифицируется дескриптором (целым числом), который используется в системных вызовах для чтения, записи и управления соединением. Каждый сокет ассоциирован с конкретным сетевым протоколом и адресом (IP-адресом и портом).
¶Адресация
Для работы с IPv4 используется структура sockaddr_in, содержащая:
- семейство адресов (
AF_INET— для IPv4,AF_INET6— для IPv6), - номер порта (16-битное целое, например, 80 для HTTP),
- IP-адрес (32-битное целое для IPv4, 128-битное для IPv6).
Для IPv6 применяется структура sockaddr_in6, а для локальных (Unix-сокетов) — sockaddr_un.
¶Типы сокетов
Основные типы, определяющие характер передачи данных:
- SOCK_STREAM — потоковый сокет, обеспечивающий надёжную двустороннюю передачу данных с установлением соединения. Обычно использует протокол TCP. Гарантирует доставку, порядок и отсутствие дублирования данных.
- SOCK_DGRAM — дейтаграммный сокет, работающий без установления соединения. Использует протокол UDP. Данные передаются в виде отдельных сообщений (датаграмм), доставка не гарантируется.
- SOCK_RAW — сырой сокет, позволяющий отправлять и принимать пакеты на уровне сетевых протоколов (например, ICMP, IP). Требует привилегий суперпользователя.
¶Протоколы
Сокет привязывается к конкретному протоколу, который задаётся при создании. Наиболее распространённые:
- TCP (IPPROTO_TCP) — для потоковых сокетов,
- UDP (IPPROTO_UDP) — для дейтаграммных,
- ICMP (IPPROTO_ICMP) — для сырых сокетов.
¶Основные системные вызовы
¶Создание и закрытие
socket(int domain, int type, int protocol)— создаёт сокет и возвращает его дескриптор. Параметры: семейство адресов (например,AF_INET), тип сокета (SOCK_STREAM), протокол (обычно 0 для автоматического выбора).close(int sockfd)— закрывает сокет и освобождает ресурсы.
¶Работа с адресом
bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)— привязывает сокет к локальному адресу (IP-адресу и порту). Обязателен для серверных сокетов.listen(int sockfd, int backlog)— переводит сокет в режим ожидания входящих соединений. Параметрbacklogзадаёт максимальную длину очереди ожидающих соединений.accept(int sockfd, struct sockaddr addr, socklen_t addrlen)— принимает входящее соединение, создавая новый сокет для обмена данными с клиентом. Блокирует выполнение до появления соединения.
¶Установка соединения (клиент)
connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)— устанавливает соединение с удалённым сервером. Используется для потоковых сокетов.
¶Передача данных
send(int sockfd, const void *buf, size_t len, int flags)— отправляет данные через установленное соединение (для TCP).sendto(int sockfd, const void buf, size_t len, int flags, const struct sockaddr dest_addr, socklen_t addrlen)— отправляет датаграмму по указанному адресу (для UDP).recv(int sockfd, void *buf, size_t len, int flags)— принимает данные из соединения (TCP).recvfrom(int sockfd, void buf, size_t len, int flags, struct sockaddr src_addr, socklen_t *addrlen)— принимает датаграмму и сохраняет адрес отправителя (UDP).
¶Дополнительные функции
setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)— устанавливает опции сокета (например,SO_REUSEADDRдля повторного использования порта).getsockopt(int sockfd, int level, int optname, void optval, socklen_t optlen)— получает текущие значения опций.getaddrinfo(const char node, const char service, const struct addrinfo *hints, struct addrinfo **res)— преобразует доменное имя и имя службы в структуры адресов, упрощая создание сокетов (рекомендуется вместо устаревшейgethostbyname).
¶Модели взаимодействия
¶Клиент-серверная модель
Наиболее распространённая архитектура, где сервер создаёт сокет, привязывает его к адресу, переводит в режим ожидания и принимает соединения. Клиент создаёт сокет и подключается к серверу. После установки соединения обе стороны могут обмениваться данными.
¶Модель без установления соединения
Используется с дейтаграммными сокетами (UDP). Сервер привязывает сокет, после чего может принимать датаграммы от любых клиентов. Клиент отправляет датаграммы на сервер без предварительного соединения. Каждая датаграмма обрабатывается независимо.
¶Примеры использования
¶Простой TCP-сервер на C (псевдокод)
``c int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(8080); bind(server_fd, (struct sockaddr*)&address, sizeof(address)); listen(server_fd, 3); int client_fd = accept(server_fd, NULL, NULL); char buffer[1024] = {0}; recv(client_fd, buffer, 1024, 0); send(client_fd, "Hello", 5, 0); close(client_fd); close(server_fd); ``
¶Простой UDP-клиент на C (псевдокод)
``c int sock = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); sendto(sock, "Hello", 5, 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); char buffer[1024] = {0}; recvfrom(sock, buffer, 1024, 0, NULL, NULL); close(sock); ``
¶Влияние и наследие
¶Стандартизация
Berkeley sockets стали основой для POSIX-стандарта сетевого программирования. Все современные Unix-подобные системы (Linux, macOS, FreeBSD, Solaris) поддерживают этот API. В Windows реализован интерфейс Winsock, который, хотя и имеет отличия, следует той же модели.
¶Альтернативы
В некоторых системах существуют альтернативные интерфейсы, например, Transport Layer Interface (TLI) в System V, но они не получили такого широкого распространения. В современных приложениях часто используются более высокоуровневые библиотеки (libcurl, Boost.Asio, Python sockets), которые, однако, в конечном счёте опираются на системные вызовы Berkeley sockets.
¶Критика
Основные недостатки интерфейса включают:
- Сложность работы с неблокирующими операциями и асинхронным вводом-выводом (требуется использование
select,poll,epollилиkqueue). - Низкоуровневость, требующая от программиста ручного управления буферами и обработки ошибок.
- Отсутствие встроенной поддержки безопасности (шифрование, аутентификация) — это реализуется на более высоком уровне (например, через OpenSSL).
¶Интересные факты
- Первоначально сокеты были разработаны для поддержки протоколов TCP/IP, но впоследствии были адаптированы для других протоколов (например, Unix-сокеты для локального межпроцессного взаимодействия).
- Термин «сокет» (socket) был заимствован из электрической инженерии, где он обозначает разъём для подключения.
- В 1980-х годах интерфейс сокетов был критически важен для распространения стека TCP/IP в академической среде и последующего превращения его в стандарт Интернета.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →


