Linux
2019-08-02
Aliases: AF_LOCAL(7), AF_UNIX(7), PF_LOCAL(7), PF_UNIX(7)
man-pages-ru
Russian man pages from the Linux Documentation Project
manpages
Manual pages about using a GNU/Linux system
man-pages
Linux kernel and C library user-space interface documentation
ИМЯ
unix - сокеты для локального межпроцессного взаимодействия
ОБЗОР
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/un.h>
unix_socket = socket(AF_UNIX, type, 0);
error = socketpair(AF_UNIX, type, 0, int *sv);
error = socketpair(AF_UNIX, type, 0, int *sv);
ОПИСАНИЕ
Семейство сокетов AF_UNIX (также известное, как AF_LOCAL) используется для эффективного взаимодействия между процессами на одной машине. Доменные сокеты UNIX могут быть как безымянными, так и иметь имя файла в файловой системе (типизированный сокет). В Linux также поддерживается абстрактное пространство имён, которое не зависит от файловой системы.
Допустимые типы сокета для домена UNIX: потоковый сокет SOCK_STREAM, датаграмный сокет SOCK_DGRAM, сохраняющий границы сообщений (в большинстве реализаций UNIX, доменные датаграмные сокеты UNIX всегда надёжны и не меняют порядок датаграмм); и (начиная с Linux 2.6.4) ориентированный на соединение задающий последовательность пакетам сокет SOCK_SEQPACKET, сохраняющий границы сообщений и доставляющий сообщения в том же порядке, в каком они были отправлены.
Доменные сокеты UNIX поддерживают передачу файловых дескрипторов или учётных данных (credentials) о процессе другим процессам, используя вспомогательные (ancillary) данные.
Формат адреса
Адрес доменного сокета UNIX представляет собой следующую структуру:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* имя пути */ };
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* имя пути */ };
Поле sun_family всегда содержит AF_UNIX. В Linux размер sun_path равен 108 байтам; также смотрите ЗАМЕЧАНИЯ ниже.
В различных системных вызовах (например, bind(2), connect(2) и sendto(2)) в качестве входных данных используется параметр sockaddr_un. Другие системные вызовы (например, getsockname(2), getpeername(2), recvfrom(2) и accept(2)) возвращают результат в параметре этого типа.
В sockaddr_un структуре различают три типа адресов:
* | с именем пути: доменный сокет UNIX может быть привязан к имени пути (с завершающимся null) в файловой системе с помощью bind(2). При возврате адреса имени пути сокета (одним и системных вызовов, упомянутых выше), его длина равна |
offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1 | |
и sun_path содержит путь, оканчивающийся null (в Linux, указанное выше выражение offsetof() равно sizeof(sa_family_t), но в некоторых реализациях включаются другие поля перед sun_path, поэтому выражение offsetof() описывает размер адресной структуры более переносимым способом). | |
Дополнительную информацию о путях сокета смотрите далее. | |
* | безымянный: Потоковый сокет, который не привязан к имени пути с помощью bind(), не имеет имени. Аналогично, два сокета, создаваемые socketpair(), также не имеют имён. При возврате адреса сокета его длина равна sizeof(sa_family_t), а значение sun_path не используется. |
* | абстрактный: абстрактный адрес сокета отличается (от имени пути сокета) тем, что значением sun_path[0] является байт null (\(aq\0\(aq). Адрес сокета в этом пространстве имён определяется дополнительными байтами в sun_path, количество которых определяется длиной указанной структуры адреса. Байты null в имени не имеют специального значения. Имя не связано с именем пути в файловой системе. При возврате адреса абстрактного сокета возвращаемое значение addrlen больше чем sizeof(sa_family_t) (т.е. больше 2), а имя сокета содержится в первых (addrlen - sizeof(sa_family_t)) байтах sun_path. |
Путевые сокеты
При привязке сокета к пути для максимальной переносимости и простоте кодирования нужно учесть несколько правил:
Есть несколько реализаций по работе с адресами доменных сокетов UNIX, которые не следуют данным правилам. Например, в некоторых реализациях (но не во всех) добавляется конечный null, если если его нет в sun_path.
* | Имя пути в sun_path должно завершаться null. |
* | Длина имени пути, включая завершающий байт null, не должна превышать размер sun_path. |
* | Аргумент addrlen, описывающий включаемую структуру sockaddr_un, должен содержать значение, как минимум: |
offsetof(struct sockaddr_un, sun_path)+strlen(addr.sun_path)+1 |
|
или, проще говоря, для addrlen можно использовать sizeof(struct sockaddr_un). |
При написании переносимых приложений учтите, что в некоторых реализациях размер sun_pathравен 92 байтам.
Различные системные вызовы (например, accept(2), recvfrom(2), getsockname(2), getpeername(2)) возвращают адресные структуры сокета. В случае с доменными сокетами UNIX аргумент значение-результат addrlen, передаваемый вызову, должен быть инициализирован как описано выше. При возврате в аргументе содержится реальный размер адресной структуры. Вызывающий должен проверить полученное значение этого аргумента: если оно превышает значение до вызова, то не гарантируется наличие конечного null в sun_path (смотрите ДЕФЕКТЫ).
Пути к сокетам и права
В реализации Linux учитываются права на каталоги, в которых располагаются сокеты. Создание нового сокета завершается ошибкой, если процесс не имеет права писать или искать (выполнять) в каталог, в котором создаётся сокет.
В Linux для подключения к объекту потокового сокета требуются права на запись в этот сокет; схожим образом, для отправки дейтаграммы в дейтаграммный сокет требуются права на запись в этот сокет. В POSIX ничего не сказано о влиянии прав файла сокета и в некоторых системах (например, в старых BSD) права на сокет игнорируются. Переносимые программы не должны полагаться на это свойство для обеспечения безопасности.
При создании нового сокета владелец и группа файла сокета назначаются согласно обычных правил. К файлу сокета разрешается любой доступ кроме выключенного процессом с помощью umask(2).
Абстрактные сокеты
Права на сокеты не учитываются у абстрактных сокетов: umask(2) процесса не учитывается при подключении к абстрактному сокету как и изменение владельца и прав доступа к объекту (посредством fchown(2) и fchmod(2)) не влияют на доступность сокета.
Абстрактные сокеты автоматически исчезают при закрытии всех открытых ссылок на них.
Пространство имён абстрактных сокетов является непереносимым расширением Linux.
Параметры сокета
В силу исторических причин эти параметры сокетов относятся к типу SOL_SOCKET, даже если они относятся к AF_UNIX. Они могут быть установлены с помощью setsockopt(2) и прочитаны с помощью getsockopt(2); тип SOL_SOCKET указывается в качестве семейства сокета.
SO_PASSCRED | |
Разрешает приём учётных данных посылающего процесса в вспомогательном сообщении SCM_CREDENTIALS каждого последующего принятого сообщения. Полученные учётные данные были заданы отправителем с помощью SCM_CREDENTIALS, или имеют значение по умолчанию, которое содержит PID отправителя, фактический пользовательский и групповой ID, если отправитель не задал вспомогательные данные SCM_CREDENTIALS. | |
Если при включении этого параметра сокет ещё не соединён, то в абстрактном пространстве имён будет автоматически создано уникальное имя. | |
Значение передаётся в аргументе setsockopt(2) и возвращается в результате getsockopt(2) в виде целочисленного логического флага. | |
SO_PASSSEC | |
Разрешает приём метки безопасности SELinux однорангового сокета в вспомогательном сообщении с типом SCM_SECURITY (смотрите ниже). | |
Значение передаётся в аргументе setsockopt(2) и возвращается в результате getsockopt(2) в виде целочисленного логического флага. | |
Параметр SO_PASSSEC поддерживается для дейтаграммных доменных сокетов UNIX начиная с Linux 2.6.18; поддержка потоковых доменных сокетов UNIX добавлена в Linux 4.2. | |
SO_PEEK_OFF | |
Смотрите socket(7). | |
SO_PEERCRED | |
С параметром сокета, доступным только для чтения, возвращаются учётные данные однорангового процесса, соединённого с сокетом. Возвращаются информационные данные, которые были действительными на момент вызова connect(2) или socketpair(2). | |
Аргументом getsockopt(2) является указатель на структуру ucred; определите макрос тестирования свойств _GNU_SOURCE для получения определения этой структуры из <sys/socket.h>. | |
Использование этого параметра возможо только для соединённых потоковых сокетов AF_UNIX и потоков AF_UNIX и для дейтаграммных сокетных пар, созданных с помощью socketpair(2). |
Свойство автоматической привязки
Если в вызов bind(2) передано значение addrlen равное sizeof(sa_family_t), или для сокета, который не привязан к адресу явно, был указан параметр сокета SO_PASSCRED, то сокет автоматически привязывается к абстрактному адресу. Адрес состоит из байта null и 5 байтов символов из набора [0-9a-f]. Таким образом, максимальное количество автоматически привязываемых адресов равно 2^20 (в Linux 2.1.15, когда была добавлена автоматическая привязка, использовалось 8 байт, и, таким образом, ограничение было 2^32 адресов. В Linux 2.3.15 количество байт сократили до 5).
Программный интерфейс сокетов
В следующих параграфах описываются специфичные тонкости доменов и неподдерживаемые возможности программного интерфейса сокетов для доменных сокетов UNIX в Linux.
Доменные сокеты UNIX не поддерживают передачу внеполосных данных (флаг MSG_OOB у send(2) и recv(2)).
Флаг MSG_MORE у send(2) не поддерживается доменными сокетами UNIX.
До Linux 3.4 использование MSG_TRUNC в аргументе flags у recv(2) не поддерживалось доменными сокетами UNIX.
Параметр сокета SO_SNDBUF учитывается в доменных сокетах UNIX, а параметр SO_RCVBUF — нет. Для датаграмных сокетов значение SO_SNDBUF считается максимальным размером для исходящих датаграмм. Это ограничение, вычисляемое как удвоенное значение (см. socket(7)) параметра, содержит меньше 32 байт накладных расходов.
Вспомогательные сообщения
Вспомогательные данные отправляются и принимаются с помощью sendmsg(2) и recvmsg(2). В силу исторических причин перечисленные типы вспомогательных сообщений относятся к типу SOL_SOCKET, даже если они относятся к AF_UNIX. Для того, чтобы отправить их, установите значение поля cmsg_level структуры cmsghdr равным SOL_SOCKET, а в значении поля cmsg_type укажите его тип. Дополнительная информация приведена в cmsg(3).
Предположим, что получатель теперь выполняет каждый вызов recvmsg(2) с буфером размером 20 байтов. Первый вызов получит пять байтов данных вместе с вспомогательными данными, отправленными вторым вызовом sendmsg(2). Следующий вызов получит оставшиеся пять байтов данных.
SCM_RIGHTS | |||||||
Передать или принять набор открытых файловых дескрипторов из другого процесса. Часть с данными содержит целочисленный массив файловых дескрипторов. | |||||||
Обычно, эта операция упоминается как «передача дескриптора файла» другому процессу. Но если точнее, то передается ссылка на открытое файловое описание (смотрите open(2)) и в принимающем процессе будет использоваться, вероятно, файловый дескриптор с другим номером. Семантически, эта операция эквивалентна дублированию (dup(2)) файлового дескриптора в таблицу файловых дескрипторов другого процесса. | |||||||
Если используемый для приёма вспомогательных данных с файловыми дескрипторами буфер слишком мал (или отсутствует), то вспомогательные данные обрезаются (или отбрасываются), а избыточные файловые дескрипторы автоматически закрываются в принимающем процессе. | |||||||
Если количество файловых дескрипторов, полученных во вспомогательных данных, превышает ограничение ресурса процесса RLIMIT_NOFILE (смотрите getrlimit(2)), то превысившие файловые дескрипторы автоматически закрываются в принимающем процессе. | |||||||
Константой ядра SCM_MAX_FD задаётся ограничение на количество файловых дескрипторов в массиве. Попытка послать с помощью sendmsg(2) массив превышающий ограничение завершается ошибкой EINVAL. Значение SCM_MAX_FD равно 253 (или 255 в ядрах до версии 2.6.38). | |||||||
SCM_CREDENTIALS | |||||||
Передать или принять учётные данные UNIX. Может быть использована для аутентификации. Учётные данные передаются в виде структуры struct ucred вспомогательного сообщения. Эта структура определена в <sys/socket.h> следующим образом: | |||||||
struct ucred { pid_t pid; /* идентификатор посылающего процесса */ uid_t uid; /* идентификатор пользователя посылающего процесса */ gid_t gid; /* идентификатор группы посылающего процесса */ }; |
|||||||
Начиная с glibc 2.8, чтобы получить определение данной структуры должен быть определён макрос тестирования свойств _GNU_SOURCE (до включения каких-либо заголовочных файлов). | |||||||
Учётные данные (credentials), указываемые отправителем, проверяются ядром. Привилегированный процесс может указывать значения, отличные от его собственных. Отправитель должен указать идентификатор своего процесса (если только он не имеет мандата CAP_SYS_ADMIN), свой реальный идентификатор пользователя, действующий идентификатор или сохранённый set-user-ID (если только он не имеет CAP_SETUID) и реальный идентификатор своей группы, действующий идентификатор группы или сохранённый set-group-ID (если только он не имеет CAP_SETGID). | |||||||
Для получения сообщения со структурой struct ucred у сокета должен быть включён параметр SO_PASSCRED. | |||||||
SCM_SECURITY | |||||||
Получить контекст безопасности SELinux (метку безопасности) однорангового сокета. Полученные вспомогательные данные представляют собой строку (с null в конце) с контекстом безопасности. Получатель должен выделить не менее NAME_MAX байт под эти данные в в части данных вспомогательного сообщения. | |||||||
Для получения контекста безопасности у сокета должен быть включён параметр SO_PASSSEC (смотрите выше). | |||||||
При отправке вспомогательных данных с помощью sendmsg(2) посылаемое сообщение может содержать только по одному элементу каждого типа, из представленных выше. | |||||||
По крайней мере один байт реальных данных должен быть отправлен при отправке вспомогательных данных. В Linux это требуется для успешной отправки вспомогательных данных через потоковый доменный сокет UNIX. При отправке вспомогательных данных через дейтаграммный доменный сокет UNIX в Linux необязательно отправлять какие-либо реальные сопровождающие данные. Однако переносимые приложения должны также включать, по крайней мере, один байт реальных данных при отправке вспомогательных данных через дейтаграммный сокет. | |||||||
При получении из потокового сокета вспомогательные данные формируют своего рода барьер для полученных данных. Например, предположим, что отправитель передает так: | |||||||
|
Если место, выделенное для получения входящих вспомогательных данных, слишком маленькое, то вспомогательные данные обрезаются по количеству заголовков, которые влезут в предоставленной буфер (или, в случае списка файловых дескрипторов SCM_RIGHTS, может быть обрезан список файловых дескрипторов). Если для входящих вспомогательных данных буфер не был предусмотрен (т. е., поле msg_control в структуре msghdr, указанное recvmsg(2), равно NULL), то входящие вспомогательные данные отбрасываются. В обоих случаях, в возвращаемом значении recvmsg(2) в msg.msg_flags будет установлен флаг MSG_CTRUNC.
Вызовы ioctl
Следующие вызовы ioctl(2) возвращают информацию в аргументе value. Корректный синтаксис:
int value; error = ioctl(unix_socket, ioctl_type, &value);
Значением ioctl_type может быть:
SIOCINQ | |
Для сокетов SOCK_STREAM этот вызов возвращает количество непрочитанных данных в приёмном буфере. Сокет не должен быть в состоянии LISTEN, иначе возвращается ошибка (EINVAL). Значение SIOCINQ определено в <linux/sockios.h>. В качестве альтернативы можно использовать синоним FIONREAD, определённый в <sys/ioctl.h>. Для сокетов SOCK_DGRAM возвращаемое значение совпадает с дейтаграммными доменными сокетами Интернета; смотрите udp(7). |
ОШИБКИ
EADDRINUSE | |
Заданный локальный адрес уже используется, или сокетный объект файловой системы уже существует. | |
EBADF | Эта ошибка может возникать в sendmsg(2) при отправке файлового дескриптора в вспомогательных данных через доменный сокет UNIX (смотрите описание SCM_RIGHTS выше), и указывает на то, что отправляемый номер файлового дескриптора некорректен (например, не является открытым файловым дескриптором). |
ECONNREFUSED | |
Удалённый адрес, указанный connect(2) не является слушающим сокетом. Эта ошибка также может возникнуть, если путь назначения не является сокетом. | |
ECONNRESET | |
Удалённый сокет был неожиданно закрыт. | |
EFAULT | Некорректный адрес пользовательской памяти. |
EINVAL | Передан неправильный аргумент. Основная причина — не задано значение AF_UNIX в поле sun_type передаваемых адресов или сокет находится в некорректном состоянии для производимой операции. |
EISCONN | |
Вызов connect(2) запущен для уже соединённого сокета, или адрес назначения указывает на соединённый сокет. | |
ENOENT | Путь, указанный в удалённом адресе для connect(2), не существует. |
ENOMEM | Не хватает памяти. |
ENOTCONN | |
Для операции над сокетом требуется адрес назначения, а сокет не соединён. | |
EOPNOTSUPP | |
Вызвана потоковая операция для не потокового сокета, или произведена попытка использования параметра для внеполосных данных. | |
EPERM | Отправитель указал неправильную информацию (credentials) в структуре struct ucred. |
EPIPE | Удалённый сокет был закрыт в потоковом сокете. Если разрешено, также будет послан сигнал SIGPIPE. Этого можно избежать, передав флаг MSG_NOSIGNAL при вызове send(2) или sendmsg(2). |
EPROTONOSUPPORT | |
Указанный протокол не является AF_UNIX. | |
EPROTOTYPE | |
Удалённый сокет не совпадает с типом локального сокета (SOCK_DGRAM против SOCK_STREAM). | |
ESOCKTNOSUPPORT | |
Неизвестный тип сокета. | |
ETOOMANYREFS | |
Эта ошибка может возникнуть в sendmsg(2) при передаче через доменный сокет UNIX в качестве вспомогательных данных файлового дескриптора (смотрите описание SCM_RIGHTS выше). Это происходит, если количество файловых дескрипторов «в полёте» превышает ограничитель ресурса RLIMIT_NOFILE и вызывающий не имеет мандата CAP_SYS_RESOURCE. Файловым дескриптором в полёте считается посланный с помощью sendmsg(2), но ещё не принятый процессом-получателем с помощью recvmsg(2). | |
Данная ошибка выявляется начиная с Linux 4.5 (и в некоторых старых версиях, в которые перенесено исправление). В ранних версиях ядра было возможно получить неограниченное количество файловых дескрипторов в полёте, посылая каждый файловый дескриптор с помощью sendmsg(2) и затем закрывая файловый дескриптор, и таким образом он не учитывался в ограничителе ресурса RLIMIT_NOFILE. |
ВЕРСИИ
SCM_CREDENTIALS и абстрактное пространство имён появились в Linux 2.2 и не должны использоваться в переносимых программах. Некоторые клоны BSD также поддерживают передачу дополнительной информации (credential), но методы реализации передачи могут серьезно отличаться на разных системах.
ЗАМЕЧАНИЯ
Привязка сокета к имени файла создаёт сокет в файловой системе, который должен быть удалён создателем, когда необходимость в нём отпадёт (с помощью unlink(2)). Обычная система ссылок UNIX также подходит для работы с сокетами; сокет может быть удалён в любое время, а реальное удаление из файловой системы будет произведено при закрытии последней на него ссылки.
Для передачи файловых дескрипторов или учётных данных (credentials) через сокет SOCK_STREAM необходимо передать или принять, по меньшей мере, один байт не вспомогательных данных в том же вызове sendmsg(2) или recvmsg(2).
В потоковых доменных сокетах UNIX отсутствует такое понятие как внеполосные данные.
ДЕФЕКТЫ
При привязке сокета к адресу Linux является одной из реализаций, которые добавляют конечный null, если он отсутствует в sun_path. В большинстве случаев в этом нет проблемы: когда адрес сокета возвращается, он будет на один байт длиннее чем был перед привязкой сокета. Однако такое неожиданное поведение может привести к следующему: если передаётся 108 не-null байтов при привязке сокета, то с дополнительным конечным null пути превышает длину sizeof(sun_path). В последствии при возврате адреса сокета (например, из accept(2)), если входной аргумент addrlen перед вызовом был равен sizeof(struct sockaddr_un), то в sun_path возвращаемой структуры адреса будет отсутствовать конечный null.
Также, некоторые реализации не требуют наличия конечного null при привязке сокета (для определения длины sun_path используется аргумент addrlen) и когда в этих реализациях возвращается адрес сокета, то в sun_path также отсутствует конечный null.
Приложения, которые получают адрес сокета могут содержать код (переносимый) для обработки случая, когда нет конечного null в sun_path, учитывая фактическое количество пригодных байт в пути:
strnlen(addr.sun_path, addrlen - offsetof(sockaddr_un, sun_path))
Или же приложение может перед получением адреса сокета выделить буфер размера sizeof(struct sockaddr_un)+1, который будет обнулён перед возвращением. Возвращающий вызов может задать в addrlen значение sizeof(struct sockaddr_un), и дополнительный нулевой байт здесь будет конечным null в строке, возвращаемой в sun_path:
void *addrp;
addrlen = sizeof(struct sockaddr_un); addrp = malloc(addrlen + 1); if (addrp == NULL)
/* обработка ошибки */ ; memset(addrp, 0, addrlen + 1);
/* обработка ошибки */ ; memset(addrp, 0, addrlen + 1);
if (getsockname(sfd, (struct sockaddr *) addrp, &addrlen)) == -1)
/* обработка ошибки */ ;
/* обработка ошибки */ ;
printf("sun_path = %s\n", ((struct sockaddr_un *) addrp)->sun_path);
Данного беспорядка можно избежать, если гарантировать, что приложения, создающие путевые сокеты, следуют правилам, описанным в общих чертах выше в Путевые сокеты.
ПРИМЕР
В следующем коде демонстрируется использование пакето-упорядочивающих сокетов для локального межпроцессного обмена. Он состоит из двух программ. Программа-сервер ждёт подключения программы-клиента. Клиент посылает свой каждый аргумент командной строки в виде отдельного сообщения. Сервер считает входящие сообщения как целые числа и складывает их. Клиент посылает строку-команду «END». Сервер посылает ответное сообщение, содержащее сумму чисел клиента. Клиент печатает сумму и завершает работу. Сервер ждёт подключение следующего клиента. Для остановки сервера, клиент вызывается с аргументом командной строки «DOWN».
Следующий вывод был записан при работе сервера в фоновом режиме и повторяющемся запуске клиента. Выполнение программы-сервера завершилось после получения им команды «DOWN».
Пример вывода
$ ./server & [1] 25887 $ ./client 3 4 Результат = 7 $ ./client 11 -5 Результат = 6 $ ./client DOWN Результат = 0 [1]+ Done ./server $
Исходный код программы
/*
* Файл connection.h
*/
* Файл connection.h
*/
#define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket" #define BUFFER_SIZE 12
/*
* Файл server.c
*/
* Файл server.c
*/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include "connection.h"
int main(int argc, char *argv[]) {
struct sockaddr_un name;
int down_flag = 0;
int ret;
int connection_socket;
int data_socket;
int result;
char buffer[BUFFER_SIZE];
struct sockaddr_un name;
int down_flag = 0;
int ret;
int connection_socket;
int data_socket;
int result;
char buffer[BUFFER_SIZE];
/*
* Удалить сокет, оставшийся после последнего
* некорректного завершения программы.
*/
* Удалить сокет, оставшийся после последнего
* некорректного завершения программы.
*/
unlink(SOCKET_NAME);
/* Создание локального сокета. */
connection_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (connection_socket == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (connection_socket == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* Для переносимости очищаем всю структуру, так как в некоторых
* реализациях имеются дополнительные (нестандартные) поля.
*/
* Для переносимости очищаем всю структуру, так как в некоторых
* реализациях имеются дополнительные (нестандартные) поля.
*/
memset(&name, 0, sizeof(struct sockaddr_un));
/* Привязываем сокет к имени сокета. */
name.sun_family = AF_UNIX;
strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
ret = bind(connection_socket, (const struct sockaddr *) &name,
sizeof(struct sockaddr_un));
if (ret == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
sizeof(struct sockaddr_un));
if (ret == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
/*
* Готовимся принимать подключения. Размер очереди (backlog)
* устанавливаем равным 20. Пока один запрос обрабатывается, другие
* запросы смогут подождать.
*/
* Готовимся принимать подключения. Размер очереди (backlog)
* устанавливаем равным 20. Пока один запрос обрабатывается, другие
* запросы смогут подождать.
*/
ret = listen(connection_socket, 20);
if (ret == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
if (ret == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
/* Основной цикл обработки подключений. */
for (;;) {
/* Ожидание входящих подключений. */
data_socket = accept(connection_socket, NULL, NULL);
if (data_socket == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
if (data_socket == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
result = 0;
for (;;) {
for (;;) {
/* Ожидание следующего пакета с данными. */
ret = read(data_socket, buffer, BUFFER_SIZE);
if (ret == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (ret == -1) {
perror("read");
exit(EXIT_FAILURE);
}
/* Проверяем, что буфер завершается 0. */
buffer[BUFFER_SIZE - 1] = 0;
/* Обработка команд. */
if (!strncmp(buffer, "DOWN", BUFFER_SIZE)) {
down_flag = 1;
break;
}
down_flag = 1;
break;
}
if (!strncmp(buffer, "END", BUFFER_SIZE)) {
break;
}
break;
}
/* Добавляем полученную команду. */
result += atoi(buffer);
}
}
/* Отправка результата. */
sprintf(buffer, "%d", result);
ret = write(data_socket, buffer, BUFFER_SIZE);
if (ret == -1) {
perror("write");
exit(EXIT_FAILURE);
}
ret = write(data_socket, buffer, BUFFER_SIZE);
if (ret == -1) {
perror("write");
exit(EXIT_FAILURE);
}
/* Закрытие сокета. */
close(data_socket);
/* Завершаем работу по команде DOWN. */
if (down_flag) {
break;
}
}
break;
}
}
close(connection_socket);
/* Удаляем сокет. */
unlink(SOCKET_NAME);
exit(EXIT_SUCCESS); }
/*
* Файл client.c
*/
* Файл client.c
*/
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include "connection.h"
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
int i;
int ret;
int data_socket;
char buffer[BUFFER_SIZE];
struct sockaddr_un addr;
int i;
int ret;
int data_socket;
char buffer[BUFFER_SIZE];
/* Создание локального сокета. */
data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (data_socket == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (data_socket == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* Для переносимости очищаем всю структуру, так как в некоторых
* реализациях имеются дополнительные (нестандартные) поля.
*/
* Для переносимости очищаем всю структуру, так как в некоторых
* реализациях имеются дополнительные (нестандартные) поля.
*/
memset(&addr, 0, sizeof(struct sockaddr_un));
/* соединяем сокет с адресом сокета */
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
ret = connect (data_socket, (const struct sockaddr *) &addr,
sizeof(struct sockaddr_un));
if (ret == -1) {
fprintf(stderr, "The server is down.\n");
exit(EXIT_FAILURE);
}
sizeof(struct sockaddr_un));
if (ret == -1) {
fprintf(stderr, "The server is down.\n");
exit(EXIT_FAILURE);
}
/* Посылаем аргументы. */
for (i = 1; i < argc; ++i) {
ret = write(data_socket, argv[i], strlen(argv[i]) + 1);
if (ret == -1) {
perror("write");
break;
}
}
ret = write(data_socket, argv[i], strlen(argv[i]) + 1);
if (ret == -1) {
perror("write");
break;
}
}
/* Отправка результата. */
strcpy (buffer, "END");
ret = write(data_socket, buffer, strlen(buffer) + 1);
if (ret == -1) {
perror("write");
exit(EXIT_FAILURE);
}
ret = write(data_socket, buffer, strlen(buffer) + 1);
if (ret == -1) {
perror("write");
exit(EXIT_FAILURE);
}
/* Получение результата. */
ret = read(data_socket, buffer, BUFFER_SIZE);
if (ret == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (ret == -1) {
perror("read");
exit(EXIT_FAILURE);
}
/* Проверяем, что буфер завершается 0. */
buffer[BUFFER_SIZE - 1] = 0;
printf("Result = %s\n", buffer);
/* Закрытие сокета. */
close(data_socket);
exit(EXIT_SUCCESS); }
Пример использования SCM_RIGHTS приведён в cmsg(3).
СМОТРИТЕ ТАКЖЕ
REFERENCED BY
CURLOPT_ABSTRACT_UNIX_SOCKET(3), CURLOPT_UNIX_SOCKET_PATH(3), bind(2), getpeername(2), getrlimit(2), getsockname(2), getsockopt(2), kcmp(2), memfd_create(2), open(2), recv(2), send(2), setns(2), signalfd(2), socket(2), socketpair(2), capabilities(7), credentials(7), pid_namespaces(7), socket(7), user_namespaces(7), abduco(1), pmdaRootConnect(3), pmdaroot(1), privbind(1), socket(1), mstack(1), msocket(2viewos), vlimit(3), linkat(2), GNURLOPT_ABSTRACT_UNIX_SOCKET(3), GNURLOPT_UNIX_SOCKET_PATH(3), network_namespaces(7), termy-server(1), address_families(7)