Accept in linux sockets
int accept(int s , struct sockaddr * addr , socklen_t * addrlen );
ОПИСАНИЕ
— это сокет, который был создан с помощью socket (2), привязан к локальному адресу с помощью bind (2), и слушает соединения после listen (2).
— это указатель на структуру sockaddr . В эту структуру помещается адрес другой стороны, в том виде, в каком он известен на коммуникационном уровне. Точный формат адреса, передаваемого в параметре addr , определяется «семейством» сокета (см. socket (2) и страницу руководства по соответствующему протоколу). Аргумент addrlen является параметром, передаваемым по ссылке: перед вызовом он содержит размер структуры, на которую ссылается addr , а после вызова
— действительную длину адреса в байтах. Если addr равен NULL , он не заполняется.
Если в очереди нет запросов на соединение, и на сокет не установлен флаг, что он является неблокирующим, accept блокирует вызвавшую программу до появления соединения. Если сокет является неблокирующим, а в очереди нет запросов на соединение, то accept возвращает EAGAIN.
Для того, чтобы получать уведомления о входящих сооединениях на сокете, можно использовать select (2) или poll (2). В этом случае, когда придёт запрос на новое соединение, будет доставлено событие «можно читать», и после этого вы можете вызвать accept , чтобы получить сокет для этого соединения. Можно также настроить сокет так, чтобы он посылал сигнал SIGIO , когда на нём происходит какая-либо активность; см. socket (7), где описаны детали.
Для определённых протоколов, которые требуют явного подтверждения, например, DECNet, системный вызов accept можно рассматривать просто как извлечение из очереди следующего запроса на соединение, не подразумевающее подтверждение. Подтверждение, в свою очередь, произойдет при следующем чтении или записи в новом файловом дескрипторе, а отказ от соединения может произойти при закрытии сокета. В настоящий момент под Linux такую семантику имеет только DECNet.
ЗАМЕЧАНИЯ
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
ОБРАБОТКА ОШИБОК
ОШИБКИ
accept может завершиться с ошибкой если: Параметр EFAULT addr не находится в пространстве адресов с возможностью записи. ENOBUFS, ENOMEM Не хватает свободной памяти. Это зачастую означает, что выделение памяти ограничено размерами буфера сокетов, а не системной памятью, но это не 100% верно. EPROTO Ошибка протокола.
В Linux accept может завешиться с ошибкой если: EPERM Правила межсетевого экрана запрещают соединение.
Вдобавок, могут также возвращаться сетевые ошибки на новом сокете и ошибки, могущие возникнуть в протоколе. Различные ядра Linux могут вернуть другие ошибки, например, EMFILE , EINVAL , ENOSR , ENOBUFS , EPERM , ECONNABORTED , ESOCKTNOSUPPORT , EPROTONOSUPPORT , ETIMEDOUT , ERESTARTSYS .
СООТВЕТСТВИЕ СТАНДАРТАМ
Реализация accept в Linux _не_ наследует флаги сокетов (типа O_NONBLOCK ). Это поведение отличается от других реализаций BSD-сокетов. Переносимые программы не должны полагаться на такое поведение и всегда должны устанавливать на сокете, полученном от accept , все требуемые флаги.
ЗАМЕЧАНИЕ
4 и SGI); черновик стандарта POSIX
1003.1g пытался поменять этот тип на size_t * , и в SunOS
5 это именно так. Поздние черновики POSIX содержат socklen_t * , и в Single Unix Specification и glibc2 это именно так.
По словам Линуса Торвальдса:
В _любой_ разумной библиотеке размеры «socklen_t» и int _должны_ совпадать. Любой другой вариант разламывает реализацию BSD-сокетов. В POSIX сначала использовали size_t, но я (и, к счастью, кто-то ещё, хотя и не слишком многие) очень громко пожаловались. Такая реализация полностью сломана, как раз потому, что size_t очень редко имеет тот же размер, что и «int», например, на 64-битных архитектурах. Это необходимо потому, что интерфейс BSD-сокетов таков, каков он есть.
В любом случае, люди из POSIX наконец поняли и создали «socklen_t». Вообще, с самого начала они просто не должны были ничего трогать, но по какой-то причине они чувствовали, что должны использовать поименованный тип (вероятно, они не хотели ударить в грязь лицом, сделав глупость, поэтому они тихо переименовали место, в котором просчитались).
Источник
Accept in linux sockets
ОБЗОР
ОПИСАНИЕ
Аргумент sockfd — это сокет, который был создан с помощью socket(2), привязанный к локальному адресу с помощью bind(2), и прослушивающий соединения после listen(2).
Аргумент addr — это указатель на структуру sockaddr. В эту структуру помещается адрес ответной стороны в том виде, в каком он известен на коммуникационном уровне. Точный формат адреса, возвращаемого в параметре addr, определяется семейством адресов сокета (см. socket(2) и справочную страницу по соответствующему протоколу). Если addr равен NULL, то ничего не помещается; в этом случае addrlen не используется и также должен быть NULL.
Через аргумент addrlen осуществляется возврат результата: вызывающая сторона должна указать в нём размер (в байтах) структуры, на которую указывает addr; при возврате он будет содержать реальный размер адреса ответной стороны.
Возвращаемый адрес обрезается, если предоставленный буфер окажется слишком маленьким; в этом случае в addrlen будет возвращено значение большее чем было в вызове.
Если в очереди нет ожидающих запросов на соединение, и на сокет не помечен как неблокирующий, то accept() заблокирует вызвавшую программу до появления соединения. Если сокет помечен как неблокирующий, а в очереди нет запросов на соединение, то accept() завершится с ошибкой EAGAIN или EWOULDBLOCK.
Для того, чтобы получать уведомления о входящих соединениях на сокете, можно использовать select(2) или poll(2). В этом случае, когда придёт запрос на новое соединение, будет доставлено событие «можно читать», и после этого вы можете вызвать accept() чтобы получить сокет для этого соединения. Можно также настроить сокет так, чтобы он посылал сигнал SIGIO, когда на нём происходит какая-либо активность; см. socket(7).
Для определённых протоколов, которые требуют явного подтверждения, например, DECNet, accept() можно рассматривать просто как извлечение из очереди следующего запроса на соединение, не подразумевающее подтверждение. Подтверждение, в свою очередь, произойдет при следующем чтении или записи в новом файловом дескрипторе, а отказ от соединения может произойти при закрытии нового сокета. В настоящее время, под Linux такую семантику имеет только DECNet.
Если flags равно 0, то вызов accept4() равнозначен accept(). Следующие значения могут быть побитово сложены в flags для получения различного поведения:
SOCK_NONBLOCK Устанавливает флаг состояния файла O_NONBLOCK для нового открытого файлового дескриптора. Использование данного флага заменяет дополнительные вызовы fcntl(2) для достижения того же результата. SOCK_CLOEXEC Устанавливает флаг close-on-exec (FD_CLOEXEC) для нового открытого файлового дескриптора. Смотрите описание флага O_CLOEXEC в open(2) для того, чтобы узнать как это может пригодиться.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Обработка ошибок
ОШИБКИ
Также, Linux accept() может завершиться с ошибкой если:
EPERM Правила межсетевого экрана запрещают соединение.
Вдобавок, могут также возвращаться сетевые ошибки на новом сокете и ошибки, могущие возникнуть в протоколе. Различные ядра Linux могут возвращать другие ошибки, например, ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT, ETIMEDOUT. Значение ошибки ERESTARTSYS можно увидеть при трассировке.
ВЕРСИИ
СООТВЕТСТВИЕ СТАНДАРТАМ
accept4() является нестандартным расширением Linux.
В Linux новый сокет, возвращаемый accept(), не наследует файловые флаги состояния такие как O_NONBLOCK и O_ASYNC от прослушивающего сокета. Это поведение отличается от каноническое реализации сокетов BSD. Переносимые программы не должны полагаться на наследуемость файловых флагов состояния или её отсутствия и всегда должны устанавливать на сокете, полученном от accept(), все требуемые флаги.
ЗАМЕЧАНИЯ
Возможно не всегда будет ожидание подключения после доставки SIGIO; или select(2) или poll(2) вернут событие доступности чтения, так как подключение может быть удалено из-за асинхронной сетевой ошибкой или другая нить была вызвала раньше accept(). Это это случается, то вызов блокируется, ожидая следующего прибытия подключения. Чтобы быть уверенным, что accept() никогда не заблокируется, сокету sockfd необходимо установить флаг O_NONBLOCK (см. socket(7)).
Тип socklen_t
«_Любая_ нормальная библиотека _должна_ иметь «socklen_t» размером с int. Любой другой вариант ломает реализацию BSD-сокетов. В POSIX _сначала_ls использовали size_t, но я (и, к счастью, кто-то ещё, хотя и не слишком многие) очень громко пожаловались. Такая реализация вообще не работает, так как size_t очень редко имеет тот же размер, что и «int», например, на 64-битных архитектурах. Это необходимо _только_ потому, что так сделано в интерфейсе BSD-сокетов. В любом случае, люди из POSIX наконец поняли и создали «socklen_t». Вообще, с самого начала они просто не должны были ничего трогать, но по какой-то причине они чувствовали, что должны использовать именованный тип (вероятно, они не хотели ударить в грязь лицом сделав глупость, поэтому они тихо переименовали место, в котором просчитались).»
Источник
Сокеты в ОС Linux
В данной статье будет рассмотрено понятие сокета в операционной системе Linux: основные структуры данных, как они работают и можно ли управлять состоянием сокета с помощью приложения. В качестве практики будут рассмотрены инструменты netcat и socat.
Что такое сокет?
Сокет — это абстракция сетевого взаимодействия в операционной системе Linux. Каждому сокету соответствует пара IP-адрес + номер порта. Это стандартное определение, к которому привыкли все, спасибо вики. Хотя нет, вот здесь лучше описано. Поскольку сокет является только лишь абстракцией, то связка IP-адрес + номер порта — это уже имплементация в ОС. Верное название этой имплементации — «Интернет сокет». Абстракция используется для того, чтобы операционная система могла работать с любым типом канала передачи данных. Именно поэтому в ОС Linux Интернет сокет — это дескриптор, с которым система работает как с файлом. Типов сокетов, конечно же, намного больше. В ядре ОС Linux сокеты представлены тремя основными структурами:
struct socket — представление сокета BSD, того вида сокета, который стал основой для современных «Интернет сокетов»;
struct sock — собственная оболочка, которая в Linux называется «INET socket»;
struct sk_buff — «хранилище» данных, которые передает или получает сокет;
Как видно по исходным кодам, все структуры достаточно объемны. Работа с ними возможна при использовании языка программирования или специальных оберток и написания приложения. Для эффективного управления этими структурами нужно знать, какие типы операций над сокетами существуют и когда их применять. Для сокетов существует набор стандартных действий:
socket — создание сокета;
bind — действие используется на стороне сервера. В стандартных терминах — это открытие порта на прослушивание, используя указанный интерфейс;
listen — используется для перевода сокета в прослушивающее состояние. Применяется к серверному сокету;
connect — используется для инициализации соединения;
accept — используется сервером, создает новое соединение для клиента;
send/recv — используется для работы с отправкой/приемом данных;
close — разрыв соединения, уничтожение сокета.
Если о структурах, которые описаны выше, заботится ядро операционной системы, то в случае команд по управлению соединением ответственность берет на себя приложение, которое хочет пересылать данные по сети. Попробуем использовать знания о сокетах для работы с приложениями netcat и socat.
netcat
Оригинальная утилита появилась 25 лет назад, больше не поддерживается. На cегодняшний день существуют порты, которые поддерживаются различными дистрибутивами: Debian, Ubuntu, FreeBSD, MacOS. В операционной системе утилиту можно вызвать с помощью команды nc, nc.traditional или ncat в зависимости от ОС. Утилита позволяет «из коробки» работать с сокетами, которые используют в качестве транспорта TCP и UDP протоколы. Примеры сценариев использования, которые, по мнению автора, наиболее интересны:
перенаправление входящих/исходящих запросов;
трансляция данных на экран в шестнадцатеричном формате.
Опробуем операции в действии. Задача будет состоять в том, что необходимо отправить TCP данные через netcat в UDP соединение. Для лабораторной будет использоваться следующая топология сети:
Введем команду на открытие порта на машине Destination: nc -ulvvp 7878
Настроим машину Repeater. Так как передача из одного интерфейса этой машины будет происходить по протоколу TCP, а на другой интерфейс будет осуществляться передача по протоколу UDP, то для таких действий необходимо сделать соединитель, который сможет накапливать данные и пересылать их между открытыми портами. На такую роль отлично подходит FIFO файл. Поэтому команда для запуска будет выглядеть так: sudo mkfifo /tmp/repeater #создать FIFO файл
sudo nc -l -p 4545 > /tmp/repeater | nc -u 10.0.3.5 7878 IP адрес 10.0.3.5 — адрес машины Destination. Символы «|» и «> nc 10.0.2.4 4545
В итоге получаем возможность читать данные от машины Source:
В машине Destination:
Пример с трансляцией данных в шестнадцатеричном формате можно провести так же, но заменить команду на Destination или добавить еще один пайп на Repeater:
nc -l -p 4545 -o file
В результате будет создан файл, в котором можно будет обнаружить передаваемые данные в шестнадцатеричном формате:
Как видно из тестового сценария использования, netcat не дает контролировать практически ничего, кроме направления данных. Нет ни разграничения доступа к ресурсам, которые пересылаются, ни возможности без дополнительных ухищрений работать с двумя сокетами, ни возможности контролировать действия сокета. Протестируем socat.
socat
Инструмент, который до сих пор поддерживается и имеет весьма обширный функционал по склейке каналов для взаимодействия. Разработчиками инструмент именуется как netcat++. Ниже приведем небольшой список того что можно перенаправить через socat:
STDIO -> TCP Socket;
FILE -> TCP Socket;
TCP Socket -> Custom Application;
UDP Socket -> Custom Application;
Для повседневного использования достаточно опций, но если понадобится когда-то работать напрямую с серийным портом или виртуальным терминалом, то socat тоже умеет это делать. Полный перечень опций можно вызвать с помощью команды:
Помимо редиректов socat также можно использовать как универсальный сервер для расшаривания ресурсов, через него можно как через chroot ограничивать привилегии и доступ к директориям системы.
Чтобы комфортно пользоваться этим инструментом, нужно запомнить шаблон командной строки, который ожидает socat:
socat additionalOptions addr1 addr2
additionalOptions — опции, которые могут добавлять возможности логирования информации, управления направлением передачи данных;
addr1 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;
addr2 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;
Попробуем провести трансляцию данных из сокета в сокет. Будем использовать для этого 1 машину. Перед началом эксперимента стоит отметить, что особенностью socat является то, что для его корректной работы нужно обязательно писать 2 адреса. Причем адрес не обязательно должен быть адресом, это может быть и приложение, и стандартный вывод на экран.
Например, чтобы использовать socat как netcat в качестве TCP сервера, можно запустить вот такую команду:
socat TCP-LISTEN:4545, STDOUT
Для коннекта можно использовать netcat:
nc localhost 4545
При таком использовании, socat дает возможность пересылать сообщения в обе стороны, но если добавить флаг «-u», то общение будет только от клиента к серверу. Все серверные сообшения пересылаться не будут:
Настроим более тонко наш сервер, добавив новые опции через запятую после используемого действия:
socat TCP-LISTEN:4545,reuseaddr,keepalive,fork STDOUT
Дополнительные параметры распространяются на те действия, которые socat может выполнять по отношению к адресу. Полный список опций можно найти здесь в разделе «SOCKET option group».
Таким образом socat дает практически полный контроль над состоянием сокетов и расшариваемых ресурсов.
Статья написана в преддверии старта курса Network engineer. Basic. Всех, кто желает подробнее узнать о курсе и карьерных перспективах, приглашаем записаться на день открытых дверей, который пройдет уже 4 февраля.
Источник