В клиент-серверных вычислениях сокет домена Unix — это сокет Беркли , который позволяет обмениваться данными между двумя процессами , выполняющимися на одном и том же хост-компьютере Unix или Unix-подобном компьютере. [1] Это похоже на сокет домена Интернета , который позволяет обмениваться данными между двумя процессами, выполняющимися на разных хост-компьютерах.
Независимо от диапазона коммуникации (один и тот же хост или другой хост), [2] компьютерные программы Unix , которые выполняют коммуникацию сокетов , похожи. Единственное различие диапазона коммуникации — это метод преобразования имени в параметр адреса, необходимый для привязки соединения сокета. Для сокета домена Unix имя — . Для сокета домена Интернета имя — . В любом случае имя называется адресом . [ 3]/path/filename
IP address:Port number
Два процесса могут взаимодействовать друг с другом, если каждый получает сокет. Серверный процесс привязывает свой сокет к адресу , открывает канал прослушивания , а затем непрерывно выполняет цикл . Внутри цикла серверный процесс переходит в спящий режим, ожидая принятия клиентского соединения. [4] После принятия клиентского соединения сервер выполняет системный вызов read , который блокирует wait . Клиент подключается к сокету сервера через адрес сервера . Затем клиентский процесс записывает сообщение для серверного процесса для чтения. Алгоритм приложения может включать в себя несколько взаимодействий чтения/записи. После завершения алгоритма клиент выполняет [5] , а сервер выполняет . [6 ]exit()
close()
Для сокета домена Unix адрес сокета является /path/filename
идентификатором. Сервер создаст /path/filename
в файловой системе семафор для работы в качестве файла блокировки . В этом файле не происходит ввода-вывода, когда клиент и сервер отправляют друг другу сообщения. [7]
Сокеты впервые появились в Berkeley Software Distribution 4.2 (1983). [8] Он стал стандартом POSIX в 2000 году. [8] Интерфейс прикладного программирования был перенесен практически на все реализации Unix и большинство других операционных систем. [8]
И сервер, и клиент должны создать экземпляр объекта сокета , выполнив socket()
системный вызов . Его использование: [9]
int сокет ( int домен , int тип , int протокол );
Параметр domain
должен быть одним из следующих общих диапазонов связи : [10]
AF_UNIX
[a]AF_INET
AF_INET6
SOCK_SEQPACKET
[11]Метка сокета домена Unix используется , когда domain
значение параметра равно AF_UNIX
. Метка сокета домена Интернета используется, когда domain
значение параметра равно AF_INET
или AF_INET6
. [12]
Параметр type
должен быть одним из двух распространенных типов сокетов: поток или датаграмма. [10] Третий тип сокета доступен для экспериментального проектирования: raw.
SOCK_STREAM
создаст потоковый сокет. Потоковый сокет обеспечивает надежный, двунаправленный и ориентированный на соединение канал связи между двумя процессами. Данные передаются с использованием протокола управления передачей (TCP). [10]SOCK_DGRAM
создаст датаграммный сокет. [b] Датаграммный сокет не гарантирует надежности и не требует соединения . В результате передача происходит быстрее. Данные переносятся с использованием протокола пользовательских датаграмм (UDP). [14]SOCK_RAW
создаст сокет датаграммы Интернет-протокола (IP) . Сокет Raw пропускает транспортный уровень TCP/UDP и отправляет пакеты напрямую на сетевой уровень . [15]Для сокета домена Unix данные ( сетевые пакеты ) передаются между двумя связанными процессами через транспортный уровень — либо TCP, либо UDP. [16] Для сокета домена Интернета данные передаются между двумя связанными процессами через транспортный уровень и интернет-протокол (IP) сетевого уровня — либо TCP/IP, либо UDP/IP. [16]
Параметр protocol
должен быть установлен на ноль для потоковых и датаграммных сокетов. [2] Для необработанных сокетов protocol
параметр должен быть установлен на IPPROTO_RAW. [9]
socket_fd = сокет ( домен int , тип int , протокол int );
Как и обычный open()
системный вызов файла, socket()
системный вызов возвращает дескриптор файла . [2] [c] Суффикс возвращаемого значения _fd
обозначает дескриптор файла .
После создания нового сокета сервер привязывает сокет к адресу. Для сокета домена Unix адресом является /path/filename
.
Поскольку адрес сокета может быть либо a, /path/filename
либо an , интерфейс программирования приложенийIP_address:Port_number
сокетов требует, чтобы адрес был сначала установлен в структуру. Для сокета домена Unix структура такова: [17]
struct sockaddr_un { sa_family_t sun_family ; /* AF_UNIX */ char sun_path [ 92 ]; }
Суффикс _un
означает unix . Для интернет-доменного сокета суффикс будет либо , _in
либо _in6
. sun_
Префикс означает socket unix . [17]
Компьютерная программа для создания и привязки потокового сокета домена Unix : [7]
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <assert.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> /* Должно быть 91 символ или меньше. Некоторые Unix-подобные немного больше. */ /* Используйте каталог /tmp только для демонстрации. */ char * socket_address = "/tmp/mysocket.sock" ; void main ( void ) { int server_socket_fd ; struct sockaddr_un sockaddr_un = { 0 }; int return_value ; server_socket_fd = сокет ( AF_UNIX , SOCK_STREAM , 0 ); если ( server_socket_fd == -1 ) утвердить ( 0 ); /* Удалить (возможно) предыдущий запуск. */ remove ( socket_address ); /* Построение структуры адреса привязки. */ sockaddr_un . sun_family = AF_UNIX ; strcpy ( sockaddr_un . sun_path , socket_address ); return_value = bind ( server_socket_fd , ( struct sockaddr * ) & sockaddr_un , sizeof ( struct sockaddr_un ) ); /* Если socket_address существует в файловой системе, то привязка завершится ошибкой. */ if ( return_value == -1 ) assert ( 0 ); /* Код прослушивания и принятия пропущен. */ }
Второй параметр для bind()
— это указатель на struct sockaddr
. Однако параметр, переданный функции, — это адрес struct sockaddr_un
. struct sockaddr
— это общая структура, которая не используется. Она определена в формальном объявлении параметра для . Поскольку каждый диапазон связи имеет свой собственный фактический параметр , эта общая структура была создана как заполнитель приведения. [18]bind()
После привязки к адресу сервер открывает канал прослушивания порта, выполняя . listen()
Его использование: [19]
int listen ( int server_socket_fd , int backlog );
Фрагмент для прослушивания:
если ( прослушивать ( server_socket_fd , 4096 ) == -1 ) утверждать ( 0 );
Для сокета домена Unix , listen()
скорее всего, будет успешным и вернет 0
. Для сокета домена Интернета , если порт используется, listen()
вернет -1
. [19]
Параметр backlog
устанавливает размер очереди для ожидающих подключений. [20] Сервер может быть занят, когда клиент выполняет connect()
запрос. Запросы на подключение до этого предела будут успешными. Если переданное значение backlog превышает максимум по умолчанию, то используется максимальное значение. [19]
После открытия канала прослушивания сервер входит в бесконечный цикл . Внутри цикла находится системный вызов accept()
, который переводит себя в спящий режим. [4] Системный accept()
вызов вернет дескриптор файла, когда клиентский процесс выполнит connect()
. [21]
Фрагмент для принятия соединения:
int accept_socket_fd ; while ( 1 ) { accept_socket_fd = accept ( server_socket_fd , NULL , NULL ); if ( accept_socket_fd == -1 ) assert ( 0 ); если ( accept_socket_fd ) > 0 ) /* клиент подключен */ }
Когда accept()
возвращается положительное целое число, сервер вступает в алгоритмический диалог с клиентом.
Ввод/вывод потокового сокета может выполнять системные вызовы обычных файлов read()
и write()
. [6] Однако больше контроля доступно, если потоковый сокет выполняет системные вызовы, специфичные для сокета send()
и recv()
. В качестве альтернативы ввод/вывод датаграммного сокета должен выполнять системные вызовы, специфичные для сокета sendto()
и recvfrom()
. [22]
Для базового потокового сокета сервер получает данные с помощью read( accept_socket_fd )
и отправляет данные с помощью write( accept_socket_fd )
.
Фрагмент, иллюстрирующий ввод-вывод на базовом потоковом сокете:
int accept_socket_fd ; while ( 1 ) { accept_socket_fd = accept ( server_socket_fd , NULL , NULL ); if ( accept_socket_fd == -1 ) assert ( 0 ); если ( accept_socket_fd > 0 ) { диалог_алгоритма_сервера ( accept_socket_fd ); } } #define BUFFER_SIZE 1024void server_algorithmic_dialog ( int accept_socket_fd ) { char input_buffer [ BUFFER_SIZE ]; char output_buffer [ BUFFER_SIZE ]; чтение ( accept_socket_fd , input_buffer , BUFFER_SIZE ); если ( strcasecmp ( input_buffer , "hola" ) == 0 ) strcpy ( output_buffer , "Hola Mundo" ); иначе если ( strcasecmp ( input_buffer , "ciao" ) == 0 ) strcpy ( output_buffer , "Ciao Mondo" ); иначе strcpy ( output_buffer , "Hello World" ); запись ( accept_socket_fd , output_buffer , strlen ( output_buffer ) + 1 ); }
Алгоритмический диалог заканчивается, когда алгоритм завершается или read( accept_socket_fd )
возвращает < 1
. [6] Чтобы закрыть соединение, выполните close()
системный вызов: [6]
Фрагмент для закрытия соединения:
int accept_socket_fd ; while ( 1 ) { accept_socket_fd = accept ( server_socket_fd , NULL , NULL ); if ( accept_socket_fd == -1 ) assert ( 0 ); если ( accept_socket_fd > 0 ) { server_algorithmic_dialog ( accept_socket_fd ); закрыть ( accept_socket_fd ); } }
Фрагмент, иллюстрирующий конец диалога:
#define BUFFER_SIZE 1024void server_algorithmic_dialog ( int accept_socket_fd ) { буфер символов [ BUFFER_SIZE ]; int read_count ; /* Пропустить алгоритмический диалог */ read_count = read ( accept_socket_fd , buffer , BUFFER_SIZE ); если ( read_count < 1 ) return ; /* Пропустить алгоритмический диалог */ }
Компьютерная программа для клиента, создающая и подключающая сокет: [5]
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <assert.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> /* Должен соответствовать socket_address сервера. */ char * socket_address = "/tmp/mysocket.sock" ; void main ( void ) { int client_socket_fd ; struct sockaddr_un sockaddr_un = { 0 }; int return_value ; client_socket_fd = сокет ( AF_UNIX , SOCK_STREAM , 0 ); если ( client_socket_fd == -1 ) утвердить ( 0 ); /* Построение структуры адреса клиента. */ sockaddr_un . sun_family = AF_UNIX ; strcpy ( sockaddr_un . sun_path , socket_address ); return_value = connect ( client_socket_fd , ( struct sockaddr * ) & sockaddr_un , sizeof ( struct sockaddr_un ) ); /* Если socket_address не существует в файловой системе, */ /* или если очередь запросов на подключение сервера заполнена, */ /* то connect() завершится ошибкой. */ if ( return_value == -1 ) assert ( 0 ); /* закрыть( client_socket_fd ); <-- необязательно */ выход ( EXIT_SUCCESS ); }
Если connect()
возвращает ноль, клиент может участвовать в алгоритмическом диалоге с сервером. Клиент может отправлять потоковые данные через write( client_socket_fd )
и может получать потоковые данные через read( client_socket_fd )
.
Фрагмент, иллюстрирующий клиентский ввод-вывод на потоковом сокете:
{ /* Пропустить код конструкции */ return_value = connect ( client_socket_fd , ( struct sockaddr * ) & sockaddr_un , sizeof ( struct sockaddr_un ) ); если ( возвращаемое_значение == -1 ) утвердить ( 0 ); если ( return_value == 0 ) { client_algorithmic_dialog ( client_socket_fd ); } /* закрыть( client_socket_fd ); <-- необязательно */ /* Когда клиентский процесс завершается, */ /* если сервер пытается выполнить read(), */ /* тогда read_count будет равен 0 или -1. */ /* Это сообщение для сервера */ /* для выполнения close(). */ exit ( EXIT_SUCCESS ); } #define BUFFER_SIZE 1024void client_algorithmic_dialog ( int client_socket_fd ) { буфер символов [ BUFFER_SIZE ]; int read_count ; strcpy ( buffer , "hola" ); write ( client_socket_fd , buffer , strlen ( buffer ) + 1 ); read_count = read ( client_socket_fd , buffer , BUFFER_SIZE ); если ( read_count > 0 ) помещает ( буфер ); }
— это метод IPC, позволяющий обмениваться данными между приложениями, как на одном хосте (компьютере), так и на разных хостах, соединенных сетью.
Сервер привязывает свой сокет к известному адресу (имени), чтобы клиенты могли его найти.
Обычно серверный процесс приостанавливается при вызове accept , ожидая прибытия клиентского соединения и его принятия.
PF_UNIX
или . [11] AF означает «Address Family», а PF означает «Protocol Family».AF_LOCAL