Выбор протокола определяется связкой "пространство имен — тип взаимодействия". Поскольку для каждой такой пары, как правило, лучше всего подходит какой-то один протокол, в третьем параметре функции socket() обычно задается значение 0 (выбор по умолчанию). В случае успешного завершения функция socket() возвращает дескриптор сокета. Чтение и запись данных через сокеты осуществляется с помощью обычных файловых функций, таких как read(), write() и т.д. По окончании работы с сокетом его необходимо удалить с помощью функции close().
Чтобы установить соединение между двумя сокетами, следует на стороне клиента вызвать функцию connect(), указав адрес серверного сокета. Клиент — это процесс, инициирующий соединение, а сервер — это процесс, ожидающий поступления запросов на подключение. В первом параметре функции connect() задается дескриптор клиентского сокета, во втором— адрес серверного сокета, в третьем — длина (в байтах) адресной структуры, на которую ссылается второй параметр. Формат адреса будет разным в зависимости от пространства имен.
При работе с сокетами можно применять те же самые функции, что и при работе с файлами. О низкоуровневых функциях ввода-вывода, поддерживаемых в Linux, рассказывается в приложении Б, "Низкоуровневый ввод-вывод". Имеется также специальная функция send(), являющаяся альтернативой традиционной функции write().
5.5.3. Серверы
Жизненный цикл сервера можно представить так:
1) создание сокета, ориентированного на соединения (функция socket());
2) назначение сокету адреса привязки (функция bind());
3) перевод сокета в режим ожидания запросов (функция listen());
4) прием поступающих запросов (функция accept());
5) закрытие сокета (функция close()).
Данные не записываются и не читаются напрямую через серверный сокет. Вместо этого всякий раз, когда сервер принимает запрос на соединение, ОС Linux создает отдельный сокет, используемый для передачи данных через это соединение.
Серверному сокету необходимо с помощью функции bind() назначить адрес, чтобы клиент смог его найти. Первым аргументом функции является дескриптор сокета. Второй аргумент — это указатель на адресную структуру, формат которой будет зависеть от выбранного семейства адресов. Третий аргумент — это длина адресной структуры в байтах. После получения адреса сокет, ориентированный на соединения, должен вызвать функцию listen(), тем самым обозначив себя как сервер. Первым аргументом этой функции также является дескриптор сокета. Второй аргумент определяет, сколько запросов может находиться в очереди ожидания. Если очередь заполнена, все последующие запросы отвергаются. Этот аргумент задает не предельное число запросов, которое способен обработать сервер. а максимальное количество клиентов, которые могут находиться в режиме ожидания.
Сервер принимает от клиента запрос на подключение, вызывая функцию accept(). Первый ее аргумент — это дескриптор сокета. Второй аргумент указывает на адресную структуру, заполняемую адресом клиентского сокета. Третий аргумент содержит длину (в байтах) адресной структуры. Функция accept() создает новый сокет для обслуживания клиентского соединения и возвращает его дескриптор. Исходный серверный сокет продолжает принимать запросы от клиентов. Чтобы прочитать данные из сокета, не удалив их из входящей очереди, воспользуйтесь функцией recv(). Она принимает те же аргументы, что и функция read(), плюс дополнительный аргумент FLAGS. Флаг MSG_PEEK задает режим "неразрушающего" чтения, при котором прочитанные данные остаются в очереди.
5.5.4. Локальные сокеты
Сокеты, соединяющие процессы в пределах одного компьютера, работают в локальном пространстве имен (PF_LOCAL или PF_UNIX, это синонимы). Такие сокеты называются локальными или UNIX-сокетами. Их адресами являются имена файлов, указываемые только при создании соединения.
Имя сокета задается в структуре типа sockaddr_un. В поле sun_family необходимо записать константу AF_LOCAL, указывающую на то, что адрес находится в локальном пространстве имен. Поле sun_path содержит путевое имя файла и не может превышать 108 байтов. Длина структуры sockaddr_un вычисляется с помощью макроса SUN_LEN(). Допускается любое имя файла, но процесс должен иметь право записи в каталог, где находится файл. При подключении к сокету процесс должен иметь право чтения файла. Несмотря на то что файловая система может экспортироваться через NFS на разные компьютеры, только процессам, работающим в пределах одного компьютера, разрешается взаимодействовать друг с другом посредством локальных сокетов.