The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа
Вперед Назад Содержание

11. Гнезда

Эта глава описывает средства GNU для межпроцессорной связи, используя гнезда.

Гнездо - обобщенный межпроцессорный канал связи. Подобно каналу, гнездо представляется как дескриптор файла. Но, в отличие от каналов, гнезда поддерживает ссылка между несвязанными процессами, и даже между процессами, выполняющимися на различных машинах, которые связываются по сети. Гнезда - первичный способ связи с другими машинами; telnet, rlogin, ftp, переговоры, и другие сетевые программы используют гнезда.

Не все операционные системы поддерживают гнезда. В библиотеке GNU, заглавный файл 'sys/socket.h' существует независимо от операционной системы, и функции гнезд всегда существуют, но если система действительно не поддерживает гнезда, эти функции всегда терпят неудачу.

Незавершенность: Мы в настоящее время не описали средства для передачи сообщений или для конфигурирования интерфейса Internet.

11.1 Понятие Гнезда

Когда Вы создаете гнездо, Вы должны определить стиль связи, который Вы хотите использовать и тип протокола, который должен поддерживать ее. Стиль связи гнезда определяет семантику пользовательского уровня посылки и получения данных через гнезда. Выбор стиля связи определяет ответы на вопросы типа:

Вы должны также выбрать именное пространство для наименования гнезда. Имя гнезда ('адрес') значимо только в контексте частного namespace. Фактически, даже тип данных, используемый для имени гнезда может зависеть от именного. Именные пространства также называются 'областями', но мы избегаем этого слова, поскольку оно может быть спутано с другим использованием того же самого термина. Каждое именное пространство имеет символическое имя, которое начинается с 'PF_'. Соответствующее символическое имя, начинающееся с 'AF_' обозначает формат адреса для этого namespace.

В заключение Вы должны выбрать протокол, чтобы установить связь. Протокол определяет какой механизм низкого уровня используется, чтобы передавать и получить данные. Каждый протокол допустим для определенного именного пространства и стиля связи; именное пространство иногда называется совокупностью протоколов из-за этого имена именных пространств начинаются с 'PF_'.

Правила протокола относятся к данным, передающимся между двумя программами, возможно на различных компьютерах; большинство этих правил обработано операционной системой, и Вы не нужно знать о них. Вот что Вы должны знать относительно протоколов:

11.2 Стили Связи

Библиотека GNU поддерживает различные виды сокетов. В этом разделе описываются различные типы сокетов предоставляемые в бибилотекой GNU. Упомянутые в данном разделе символические константы определены в "sys/socket.h".

    int SOCK_STREAM (макрос)
Тип сокета SOCK_STREAM предназначен для передачи потоков байтов. Такой стиль передачи схож со стилем передачи данных через каналы (pipe) (см. Главу 10 [Трубопроводы и FIFO]). Он используется для передачи данных с отдаленным соединением. При таком стиле передачи данных обеспечивается высокая надежность.

Более подробное описание работы с использованием этого типа Вы найдете в Разделе 11.8 [Соединения].

    int SOCK_DGRAM (макрос)
Тип сокета SOCK_DGRAM используется для посылки индивидуально адресованных пакетов (ненадежен). Этот тип принципиально отличается от типа SOCK_STREAM.

Каждый раз когда Вы пишите данные в сокет этого типа, данные становятся одним пакетом. Вы должны определить адрес получателя для каждого пакета.

Единственной гарантией, которую предоставляет Вам система относительно запросов передачи данных, является то, что она пробует наилучшим образом посылать каждый пакет. У нее может получится посылка шестого пакета после неудачи с четвертыми и пятыми пакетами; седьмой пакет может прибыть перед шестым, также второй может прибыть как раз после шестого.

Типичное использование типа SOCK_DGRAM в ситуациях, где есть возможность повторной посылки пакетов, если ответ не был получен в приемлемое время.

    int SOCK_RAW  (макрос)
Этот тип обеспечивает доступ к сетевым протоколам и интерфейсам низкого уровня. Обычные пользовательские программы, обычно не имеют потребности использовать этот стиль.

11.3 Адреса сокетов

Имя сокета обычно называется адресом. Функции и символы для имеющихся адресов сокетов могли называться как с использованием термина, так и использованием термина "адрес". Вы можете расценивать эти термины как синонимичные в контексте обсуждения сокетов.

Сокет, созданный при помощи функции socket, не имеет никакого адреса. Другие процессы могут использовать его для связи только после того как Вы дадите ему адрес. Мы называем это - связывание адреса с сокетом. Связывание происходит при помощи функции bind.

В первый раз, когда Вы посылаете данные из сокета, или используете его, чтобы инициализировать соединение, система назначает адрес автоматически.

Подробности адресации сокетов изменяются, в зависимости от именного пространства, которое Вы используете. См. Раздел 11.4 [Именное пространство Файла], или Раздел 11.5 [Именное пространство Internet].

Независимо от именного пространства, Вы используете те же самые функции bind и getsockname, чтобы установить и исследовать адрес гнезда.

Форматы Адреса

Функции bind и getsockname используют обобщенный тип данных struct sockaddr *, чтобы представить указатель на адрес гнезда. Вы не можете использовать этот тип данных действительно, чтобы интерпретировать адрес или создавать его; для этого, Вы должны использовать соответствующий тип данных для именного пространства гнезда.

Таким образом, обычная нужно создать адрес в соответствующем именном пространстве специфического типа, и приводить указатель на struct sockaddr *, когда Вы вызываете bind или getsockname.

Единственная информация, которую Вы можете получить из структуры sockaddr - указатель формата адреса, который сообщает Вам какой тип данных использовать, чтобы понять адрес полностью.

Символы в этом разделе определены в заголовочном файле "sys/socket.h".

    struct sockaddr  (тип данных)
Тип структуры sockaddr непосредственно имеет следующие поля:

      short int sa_family
Это код для формата адреса. Он идентифицирует формат данных.

    сhar sa_data [14]
Это фактические данные адреса сокета, которые являются формато-зависимыми. Длина также зависит от формата, и может быть больше чем 14. Длина 14 из sa_data по существу произвольна.

Каждый формат адреса имеет символическое имя, которое начинается с "AF_ ". Каждый из них соответствует "PF_ " символу, который обозначает соответствующее именное пространство. Вот список названий форматов адресов:

AF_FILE

Обозначает формат адреса, который идет с именным пространством файла. (PF_FILE - имя этого именного пространства.) См. Раздел 11.4.2 [Подробности Именного пространства Файла], для уточнения информации относительно этого формата адреса.

AF_UNIX

Это синоним AF_FILE, для совместимости. (PF_UNIX ­ аналогично синоним для PF_FILE.)

AF_INET

Обозначает формат адреса, который идет в именном пространстве Internet. (PF_INET - имя этого именного пространства.) См. Раздел 11.5.1 [Формат Адреса Internet].

AF_UNSPEC

Не обозначает никакой специфический формат адреса. Он используется только в редких случаях, когда необходимо очистить снаружи заданный по умолчанию адрес адресата от "соединенного" датаграмного сокета. См. Раздел 11.9.1 [Посылка Датаграмм].

Соответствующий символ указателя именного пространства PF_UNSPEC существует для законченности, но нет никакой причины использовать его в программе.

"Sys/socket.h" определяет символы, начинающиеся с " AF_ " для различных видов сетей, большинство из которых фактически не встречается. Мы будем документировать только то, что действительно используется на практике.

Установка Адреса сокета

Используйте функцию bind, чтобы для связывания адреса сокета. Прототип для bind находится в заголовочном файле "sys/socket.h". Для примеров использования см. Раздел 11.4 [Именное пространство Файла].

    int bind (int socket, struct sockaddr *addr, size_t length) (функция)
Функция bind назначает адрес сокет socket. Аргументы Addr и length определяют адрес; детализированный формат адреса зависит от именного пространства. Первая часть адреса - всегда указатель формата, который определяет именное пространство, и говорит, что адрес находится в формате для этого именного пространства.

Возвращаемое значение - 0 при успехе и -1 при отказе. Для этой функции в переменной errno определены следующие виды ошибок:

EBADF

аргумент - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - сокет.

EADDRNOTAVAIL

заданный адрес не доступен на этой машине.

EADDRINUSE

Существует другой сокет использующий заданный адрес.

EINVAL

сокет уже имеет адрес.

EACCESS

Вам не достаточно прав для обращения к запрошенному адресу. (В области Internet, только супер-пользователю позволяют определить номер порта в диапазоне от 0 до IPPORT_RESERVED минус один; см. Раздел 11.5.3 [Порты].) Дополнительные условия могут быть возможны в зависимости от специфического именного пространства сокета.

Чтение Адреса сокета

Используйте функцию getsockname, чтобы исследовать адрес гнезда Internet. Прототип для этой функции находится в заголовочном файле "sys/socket.h".

    int getsockname (int socket, struct sockaddr *addr, size_t *length_ptr) (функция)
Функция getsockname возвращает информацию относительно адреса сокета в заданного аргументами addr и length_ptr. Обратите внимание, что length_ptr - указатель; Вы должны инициализировать его, как размер резервирования addr, и по возвращении он содержит фактический размер данных адреса.

Формат данных адреса зависит от именного пространства сокета. Длина информации обычно устанавливается для данного именного пространства, так что обычно Вы можете знать точно, сколько места необходимо. Обычно нужно зарезервировать место для значения, используя соответствующий тип данных для именного пространства сокета, и тогда привести адрес к struct sockaddr *, чтобы передать его getsockname.

Возвращаемое значение - 0 при успехе и -1 при ошибке. Для этой функции в переменной errno определены следующие виды ошибок:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

ENOBUFS

не имеется достаточных внутренних буферов, доступных для операции.

Вы не можете читать адрес сокета в именном пространстве файла. Это непротиворечиво с остальной частью системы; вообще, не существует способа найти имя файла из описателя для этого файла.

11.4 Именное пространство Файла

Этот раздел описывает подробности именного пространства файла, чье символическое имя (требуется, когда Вы создаете сокет) ­ PF_FILE.

Понятия Именного пространства Файла

В именном пространстве файла, адреса сокетов - имена файлов. Вы можете определять любое желаемое имя файла для адреса сокета, но Вы должны иметь право записи в каталоге, содержащем его. Для чтобы соединяться с сокетом, Вы должны иметь право чтения для него. Обычно эти файлы помещаются в каталог `/tmp'.

Одна особенность именного пространства файла -- имя используется только при открытии соединения; если только оно было законченно, адрес не значим и может не существовать.

Другая особенность заключается в том, что Вы не можете соединяться с таким сокетом на другой машине, даже если другая машина совместно использует файловую систему, которая содержит имя это имя сокета. Вы можете видеть сокет в распечатке каталога, но соединение с ним никогда не произойдет.

После того, как Вы закрываете сокет в именном пространстве файла, Вы должны удалить имя файла из файловой системы. Используйте unlink или remove, чтобы делать это; см. Раздел 9.5 [Удаление Файлов].

Именное пространство файла поддерживает только один протокол для любого типа связи; 0 - номер протокола.

Подробности Именного пространства Файла

Чтобы создавать сокет в именном пространстве файла, используйте константу PF_FILE как аргумент именного пространства для socket или socketpair. Эта константа определена в "sys/socket.h".

    int PF_FILE  (макрос)
Он обозначает именное пространство файла, в котором адреса сокетов являются именами файлов, и связываются совокупностью протоколов.

    int PF_UNIX  (макрос)
Это - синоним PF_FILE используемый для совместимости.

Структура для определения имен сокетов в именном пространстве файла определена в заголовочном файле "sys/un.h":

    struct sockaddr_un  (тип данных)
Эта структура используется, чтобы определить адреса сокета именного пространства файла. Она имеет следующие поля:

    short int sun_family
Это поле идентифицирует совокупность адреса или формат адреса сокета. Вы должны сохранить значение AF_FILE, чтобы обозначить именное пространство файла. См. Раздел 11.3 [Адреса Гнезда].

    char sun_path[108]
Это имя используемого файла.

Незавершенность: Почему - 108? RMS предлагает делать его массивом нулевой длины и использовать alloc, чтобы зарезервировать соответствующее количество памяти, основываясь на длине filename.

Вы должны вычислить параметр длины для адреса сокета в именном пространстве файла как сумму размера компоненты sun_family и длины (не размера резервирования!) строки имени файла.

Пример файлового-именного пространства сокетов

Вот пример, показывающий, как создавать и связывать сокет в именном пространстве файла.

                #include <stddef.h>
                #include <stdio.h>
                #include <errno.h>
                #include <stdlib.h>
                #include <sys/socket.h>
                #include <sys/un.h>
                int
                make_named_socket (const char *filename)
                {
                        struct sockaddr_un name;
                        int sock;
                        size_t size;
                        sock = socket (PF_UNIX, SOCK_DGRAM, 0);
                        if (sock < 0)
                                        {
                                                perror ("socket");
                                                exit (EXIT_FAILURE);
                                        }
                        name.sun_family = AF_FILE;
                        strcpy (name.sun_path, filename);
                        size=(offsetof(struct sockaddr_un, sun_path)
                                + strlen (name.sun_path) + 1);
                        if (bind (sock, (struct sockaddr *) &name,
                                        size) < 0) {
                                        perror ("bind");
                                        exit (EXIT_FAILURE);
                                }
                        return sock;
       }

11.5 Именное пространство Internet

Этот раздел описывает подробности протокола и соглашений именования сокетов, используемые в именном пространстве Internet.

Чтобы создать сокет в именном пространстве Internet, используйте символическое имя PF_INET этого именного пространства как аргумент именного пространства socket или socketpair. Эта макрокоманда определена в "sys/socket.h".

       int PF_INET        (макрос)
Обозначает именное пространство Internet и связанную совокупность протоколов.

Адрес сокета для именного пространства Internet включает следующие компоненты:

Вы должны гарантировать, что адрес и номер порта представляется в каноническиом формате, называемом сетевым байтовым порядком. См. Раздел 11.5.5 [Порядок Байов], для уточнения информации относительно этого.

Формат Адреса сокета Internet

В именном пространстве Internet, адрес состоит из главного адреса и порта на этой главной ЭВМ. Кроме того, протокол, который Вы выбираете, служит как бы частью адреса, потому что местные числа порта значимы только внутри специфического протокола.

Тип данных для представления адресов в именном пространстве Internet определен в заголовочном файле "netinet/in.h".

    struct sockaddr_in  (тип данных)
Это тип данных, используемый, чтобы представить адреса в именном пространстве Internet. Он имеет следующие поля:

    short int sin_family
Это поле идентифицирует совокупность адресов или формат адреса сокета. Вы должны сохранить значение AF_INET в этом элементе. См. Раздел 11.3 [Адреса Гнезда].

    struct in_addr sin_addr
Это Internet адрес главной машины. См. Раздел 11.5.2 [Главные Адреса], и Раздел 11.5.2.4 [Главные Имена].

    unsigned short int sin_port
Это номер порта. См. Раздел 11.5.3 [Порты].

Когда Вы вызываете bind или getsockname, Вы должны определить sizeof (struct sockaddr_in) как параметр длины при использовании адреса в именном пространстве Internet.

Главные Адреса

Каждый компьютер в Internet имеет один, или большое количество Internet адресов, т. е. числа, которые идентифицируют этот компьютер среди остальных на Internet. Пользователи обычно записывают число-адрес главной ЭВМ как последовательность из четырех чисел, отделяемых точками, например "128.52.46.32".

Каждый компьютер также имеет одно или большое количество главных имен, которые являются строками слов, отделяемых точками, например "churchy.gnu.ai.mit.edu".

Программы, которые допускают пользователю определять главную ЭВМ обычно принимают и числовые адреса и главные имена. Но для открытия соединения программе необходим числовой адрес, так что для использования главного имени, Вам нужно преобразовать его в числовой адрес.

Адреса Главной ЭВМ Internet

Адрес главной ЭВМ в Internet - это номер, содержащий четыре байта данных. Они разделены на две части, сетевой номер и местный номер внутри этой сети. Сетевой номер состоит из первых одного, двух или трех байт; остальная часть байтов - местный адрес.

Сетевые числа зарегистрированы в Сетевом Информационном Центре (NIC), и разделены на три класса A, B, и C. Местные числа сетевого адреса индивидуальных машин зарегистрированы администратором в локальной сети.

Сеть класса А имеет одиночно-байтовые числа в диапазоне от 0 до 127. Сетей класса A не так уж много, но каждая из них может поддерживать очень большое количество главных ЭВМ. Сети класса В размера имеет Двух-байтовые сетевые числа, с первым байтом в диапазоне от 128 до 191. Класс C самый маленький; адреса в нем они имеют Трех-байтовые сетевые числа, с первым байтом в диапазоне 192-255. Таким образом, первый 1, 2, или 3 байты адреса Internet определяют сеть. Оставшиеся байты адреса Internet определяют адрес внутри этой сети.

Нулевая сеть класса A зарезервирована для передачи по всем сетям. Кроме того, главный номер 0 внутри каждой сети зарезервирован для передачи на все главные ЭВМ в этой сети.

127-ая сеть класса A зарезервирована для возврата цикла; Вы можете всегда использовать адрес Internet "127.0.0.1", чтобы обратиться к главной машине.

Так как одиночная машина может быть элементом нескольких сетей, она может иметь много адресов главной ЭВМ Internet. Однако, предполагается, что существует не более одной машины с тем же самым главным адресом.

Имеются четыре формы стандартного расположения чисел и точек для Internet адреса:

  1. а.b.c.d определяет все четыре байта адреса индивидуально.
  2. а.b.c последняя часть адреса, интерпретируется как 2-байтовое число. Это полезно для определения главных адресов в сети класса B с сетевым адресом a.
  3. а если дана только одна часть, то она соответствует непосредственно числу главного адреса.
  4. "0x" или "0X" подразумевает шестнадцатеричную систему счисления; "0" подразумевает восьмеричную; в противном случае десятичная система счисления.

Тип Данных Главного Адреса

Адреса главной ЭВМ Internet представляются в некоторых контекстах как integers (long unsigned int). В других контекстах, integer упакован внутри структуры типа struct in_addr. Было бы лучше, если бы использование было сделано непротиворечивым.

Следующие базисные определения для Internet адреса, появляются в файле "netinet/in.h":

    struct in_addr      (тип данных)
Этот тип данных используется в некоторых контекстах, чтобы содержать адрес главной ЭВМ Internet. Он имеет только одно поле, именованное s_addr, в которое записывается адрес как long unsigned int.

    unsigned long int INADDR_LOOPBACK  (макрос)
Вы можете использовать эту константу, в качестве адреса вашей машины вместо того, чтобы искать настоящий адрес. В Internet это адрес "127.0.0.1", который обычно называется "localhost". Эта специальная константа сохраняет Вас от проблемы поиска адреса вашей собственной машины. Используя этот адрес можно имитировать передачу пакетов Internet в пределах одной машины.

    unsigned long int INADDR_ANY  (макрос)
Вы можете использовать эту константу вместо "любого входящего адреса". См. Раздел 11.3.2 [Установка Адреса]. Это обычный адрес, для указания в поле sin_addr структуры sockaddr_in, если Вы хотите установить соединение Internet.

    unsigned long int INADDR_BROADCAST  (макрос)
Эта константа - адрес, который Вы используете для посылки широковещательных сообщений.

    unsigned long int INADDR_NONE (макрос)
Эта константа используется некоторыми функциями для отображения ошибок.

Функции Главного Адреса

Это дополнительные функции для управления Internet адресацией, объявленые в "arpa/inet.h". Они представляют Internet адреса в сетевом порядке байтов; это сетевые числа и числа локальных сетевых адресов в главном порядке байтов. См. Раздел 11.5.5 [Порядок Байтов], для объяснения сетевого и главного порядка байтов.

       int inet_aton (const char *name, struct in_addr *addr) (функция)
Эта функция преобразовывает имя адреса главной ЭВМ Internet из стандарта числа-и-точки в двоичные данные. Inet_aton возвращает отличное от нуля чило, если адрес допустим, и нуль если нет.

    unsigned long int inet_addr (const char *name)  (функция)
Эта функция преобразовывает имя адреса главной ЭВМ Internet из стандарта числа-и-точки в двоичные данные. Если ввод не допустим, inet_addr, возвращает INADDR_NONE. Это - устаревший интерфейс для inet_aton; устаревший, потому что INADDR_NONE - допустимый адрес (255.255.255.255), и inet_aton обеспечивает более чистый способ указать ошибку.

    unsigned long int inet_network (const char *name)  (функция)
Эта функция извлекает сетевой номер из имени адреса, данного в стандарте числа-и-точки. Если ввод не допустим, inet_network, возвращает -1.

    char * inet_ntoa (struct in_addr addr)  (функция)
Эта функция преобразовывает addr Internet адреса главной ЭВМ в строку в стандарте числа-и-точки. Возвращаемое значение ­ указатель на статически размещенный буфер. Последующие обращения запишут поверх в тот же самый буфер, так что Вы должны копировать строку, если Вы должны сохранить ее.

    struct in_addr inet_makeaddr (int net, int local)  (функция)
Эта функция создает Internet адрес главной ЭВМ , объединяя номер сети с местным номером.

    int inet_lnaof (struct in_addr addr)
Эта функция возвращает локальную часть адреса, если Internet адрес главной ЭВМ - addr.

    int inet_netof (struct in_addr addr)  (функция)
Эта функция возвращает сетевую часть addr Internet адреса главной ЭВМ.

Главные Имена

Кроме стандарта числа-и-точки для Internet адреса, Вы можете также обратиться к главной ЭВМ символическим именем. Преимущество символического имени - то, что его обычно проще запомнить. Например, машина с адресом "128.52.46.32" также может иметь адрес "churchy.gnu.ai.mit.edu"; и другие машины в этом домене могут обратиться к ней просто как "churchy".

Система использует базу данных, чтобы следить за отображением между главными именами и главными числами. Эта база данных - файл, обычно "/etc/hosts" или эквивалент, обеспеченный блоком преобразования имен. Функции и другие символы для доступа к этой базе данных объявлены в "netdb.h". Возможности BSD могут использоваться при подключении файла "netdb.h".

    struct hostent  (тип данных)
Этот тип данных используется для представления доступа к базе данных главных ЭВМ. Он имеет следующие элементы:

                char *h_name
Это "официальное" имя главной ЭВМ.

    char **h_aliases
Это альтернативные имена для главной ЭВМ, представляемые как вектор с нулевым символом в конце строк.

    int h_addrtype
Это тип главного адреса; практически, значение - всегда AF_INET. В принципе другие виды адресов могли бы представляться в базе данных, также как Internet адреса; если это было выполнено, Вы могли бы найти значение в этом поле отличным от AF_INET. См. Раздел 11.3 [Адреса Гнезда].

    int h_length
Это длина, в байтах, каждого адреса.

    char **h_addr_list
Это вектор адресов для главной ЭВМ. (Заметим, что главная ЭВМ могла бы быть соединенной с несколькими сетями и иметь различные адреса в каждой.) вектор завершен нулевым указателем.

    char *h_addr
Это синоним для h_addr_list [0]; другими словами, это первый главный адрес.

В главной базе данных каждый адрес только блок памяти h_length байт длиной. Но в других контекстах имеется неявное предположение, что Вы можете преобразовывать его в struct addr_in или long unsigned int. Главные адреса в структуре struct hostent всегда даны в сетевом порядке байтов; см. Раздел 11.5.5 [Порядок Байт].

Вы можете использовать gethostbyname или gethostbyaddr, для уточнения инфрмации базы данных главных ЭВМ относительно специфической главной ЭВМ. Информация возвращена в статически размещенной структуре.

    struct hostent * gethostbyname (const char *name)  (функция)
Функция Gethostbyname возвращает информацию относительно главной ЭВМ, именованной name. Если происходит ошибка поиска, она возвращает пустой указатель.

    struct hostent * gethostbyaddr (const char *addr, int length, (функция)
Функция Gethostbyaddr возвращает информацию относительно главной ЭВМ с адресом addr в Internet. Аргумент length - размер (в байтах) адреса addr. format определяет формат адреса; для адреса Internet, определите значение AF_INET.

Если происходит сбой поиска, gethostbyaddr возвращает пустой указатель.

Если поиск имени gethostbyname или gethostbyaddr окончился неудачно, Вы можете выяснить причину, рассматривая значение переменной h_errno. (Было бы правильнее установить errno, но использование h_errno совместимо с другими системами.) Перед использованием h_errno, Вы должны объявить его примерно так:

    extern int h_errno;
Имеются коды ошибок, которые Вы можете находить в h_errno:

    HOST_NOT_FOUND
Нет такой главной ЭВМ в базе данных.

    TRY_AGAIN
Это происходит, когда с блоком преобразования имен нельзя было бы входить в контакт. Если Вы попробуете сделать это позже, то возможно Вам повезет больше.

    NO_RECOVERY
Произошла невосстанавливаемая ошибка .

    NO_ADDRESS
Главная база данных содержит вход для имени, но он не имеет связанного Internet адреса .

Вы можете также просматривать всю базу данных главных ЭВМ используя sethostent, gethostent, и endhostent. Будьте внимательны при использовании этих функций, потому что они не допускают повторного использования.

    void sethostent (int stayopen)  (функция)
Эта функция открывает базу данных главных ЭВМ для просмотра. Затем Вы можете вызывать gethostent для ее чтения.

Если аргумент stayopen является отличным от нуля, она устанавливает флаг так, чтобы последующие обращения к gethostbyname или gethostbyaddr не закрыли базу данных (что они обычно сделали бы).

Это делается для эффективности, если Вы вызываете эти функции несколько раз, то избегаете повторного открытия базы данных для каждого обращения.

    struct hostent * gethostent ()  (функция)
Эта функция возвращает следующий вход в базе данных главных ЭВМ. Она возвращает пустой указатель, если не имеется больше входов.

    void endhostent ()  (функция)
Эта функция закрывает базу данных главных ЭВМ.

Порты Internet

Адрес сокета в именном пространстве Internet состоит из адреса Internet машины плюс номер порта, который отличает гнездо на данной машине (для данного протокола). Номера портов располагаются от 0 до 65535.

Номера портов меньше, зарезервированых IPPORT_RESERVED для стандартных серверов, типа finger и telnet. Имеется база данных, которая следит за ними, и Вы можете использовать функцию getservbyname для отображения сервисного номера порта; см. Раздел 11.5.4 [База данных Услуг].

Если Вы собираетесь устанавливать сервер, который не является стандартно определенным в базе данных, то Вам необходимо выбрать для него номер порта. Используйте номера большие чем IPPORT_USERRESERVED; такие числа зарезервированы для серверов и никогда не будут генерироваться системой.

Когда Вы используете сокет без определения адреса, система генерирует номер порта для него. Этот номер попадает в интервал между IPPORT_RESERVED и IPPORT_USERRESERVED.

На Internet, фактически, законно иметь два различных сокета с одинаковыми номерами портов, пока они оба не попытаются связаться с тем этим адресом сокета (главный адрес плюс номер порта). Вы не должны дублировать номер порта за исключением специальных обстоятельств, где протокол с более высоким уровнем требует этого. Обычно, система не будет разрешать Вам делать это; bind требует различные номера портов. Чтобы многократно использовать номер порта, Вы должны установить опцию сокета SO_REUSEADDR. См. Раздел 11.11.2 [Опции Сокетов].

Эти макрокоманды определены в заголовочном файле "netinet/in.h".

    int IPPORT_RESERVED  (макрос)
Номера портов меньшие IPPORT_RESERVED зарезервированы для использования суперпользователем.

    int IPPORT_USERRESERVED  (макрос)
Номера портов большие или равные IPPORT_USERRESERVED зарезервированы для явного использования; они никогда не будут размещены автоматически.

База данных Услуг

База данных, которая следит за "общеизвестными" услугами - это обычно или файл "/etc/services" или эквивалент из блока преобразования имен. Вы можете использовать эти утилиты, объявленные в "netdb.h" для обращения к базе данных услуг.

    struct servent  (тип данных)
Этот тип данных содержит информацию относительно входов в базе данных услуг, он имеет следующие элементы:

    char *s_name
Это "официальное" имя обслуживания.

    char **s_aliases
Это альтернативные имена обслуживания, представляемые массивом строк.

Пустой указатель завершает массив.

    int s_port
Это номер порта для обслуживания. Номера портов даны в сетевом порядке байтов; см. Раздел 11.5.5 [Порядок Байтов].

    char *s_proto
Это имя протокола, для использования с этим обслуживанием. См. Раздел 11.5.6 [База данных Протоколов].

Чтобы получать информацию относительно специфического обслуживания, используйте функции getservbyname или getservbyport функции. Информация возвращается в статически размещенной структуре.

    struct servent * getservbyname (const char *name, const char *proto) (функция)
Getservbyname функция возвращает информацию относительно обслуживания, именованного name, используя протокол proto. Если она не может найти такое обслуживание, она возвращает пустой указатель.

Эта функция полезна как для серверов так и для клиентов; серверы используют ее, чтобы определить, на каком порту они должны принимать пакеты (см. Раздел 11.8.2 [Прием]).

    struct servent * getservbyport (int port, const char *proto) (функция)
Функция Getservbyport возвращает информацию относительно обслуживания на порте port, используя протокол proto. Если она не может найти такое обслуживание, она возвращает пустой указатель.

Вы можете также просматривать базу данных услуг, используя setservent, getservent, и endservent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setservent (int stayopen)  (функция)
Эта функция открывает базу данных услуг для просмотра.

Если аргумент stayopen является отличным от нуля, она устанавливает флаг так, чтобы последующие обращения к getservbyname или getservbyport не закрыли базу данных (поскольку они обычно закрыли бы). Это делается для большей эффективности, если Вы вызываете эти функции несколько раз, избегая повторного открытия базы данных для каждого обращения.

    struct servent * getservent (void)  (функция)
Эта функция возвращает следующий вход базы данных услуг. Если там нет больше входов, она возвращает пустой указатель.

    void endservent (void)  (функция)
Эта функция закрывает базу данных услуг.

Преобразование Порядка Байтов

Различные виды компьютеров используют различные соглашения для упорядочения байтов внутри слова. Некоторые компьютеры помещают старший байт сначала (это называется "big-endian" порядком), а другие помещают его последним ("little-endian" порядок).

Так, чтобы машины с различными соглашениями порядка байтов могли связываться, протоколы Internet определяют каноническое соглашение порядка байтов для данных, переданных по сети. Оно известно как сетевой порядок байта.

При установлении соединения в Internet, Вы должны удостовериться, что данные в sin_port и sin_addr элементах структуры sockaddr_in представляются в сетевом порядке байта. Если Вы кодируете данные integer в сообщениях, посланных через сокет, Вы должны преобразовать их в сетевой порядок байта. Если Вы не делаете этого, ваша программа может работать не правильно при сообщении с другими типами машин.

Если Вы используете getservbyname и gethostbyname или inet_addr, для получения номера порта и главного адреса, то эти значения уже в сетевом порядке байта, и Вы можете копировать их непосредственно в структуру sockaddr_in.

Иначе, Вы должны преобразовать значения явно. Используйте htons и ntohs, чтобы преобразовать значения для sin_port элемента. Используйте htonl и ntohl, чтобы преобразовать значения для sin_addr элемента. (Помните, struct in_addr эквивалентен long unsigned int.) Эти функции описаны в "netinet/in.h".

    unsigned short int htons (unsigned short int hostshort) (функция)
Эта функция преобразовывает short integer hostshort из главного порядка байтов в сетевой порядок байта.

    unsigned short int ntohs (unsigned short int netshort) (функция)
Эта функция преобразовывает short integer netshort из сетевого порядка байта в главный порядок байта.

    unsigned long int htonl (unsigned long int hostlong)
Эта функция преобразовывает long integer hostlong из главного порядка байтов в сетевой порядок байт.

    unsigned long int ntohl (unsigned long int netlong)  (функция)
Эта функция преобразовывает long integer netlong из сетевого порядка байт в главный порядок байт.

База данных Протоколов

Протокол связи используется для управления низкого уровня обмена данными. Например, протокол осуществляет вещи подобно контрольным суммам, чтобы обнаружить ошибки в передачах, и команды маршрутизации для сообщений.

Заданный по умолчанию протокол связи для именного пространства Internet зависит от стиля связи. Для потокового взаимодействия, значение по умолчанию - TCP ("протокол управления передачей"). Для датаграмной связи, значение по умолчанию - UDP ("протокол датаграммы пользователя"). Для надежной датаграмной связи значение по умолчанию - RDP ("надежный датаграмный протокол"). Вы должны почти всегда использовать это значение по умолчанию.

Протоколы Internet вообще определены именем вместо номера. Сетевые протоколы, которые знает главная ЭВМ, сохранены в базе данных. Она обычно происходит от файла "/etc/protocols", или может быть эквивалент, обеспеченный блоком преобразования имен. Вы можете искать номер протокола, связанный с именованным протоколом в базе данных, используя getprotobyname функцию.

Имеются детализированные описания утилит для доступа к базе данных протоколов. Они объявлены в "netdb.h".

    struct protoent  (тип данных)
Этот тип данных используется, чтобы представить входы в базе данных сетевых протоколов. Он имеет следующие элементы:

    char *p_name
Это официальное имя протокола.

    char **p_aliases
Это альтернативные имена для протокола, заданные как массив строк.

Последний элемент массива - пустой указатель.

    int p_proto
Это номер протокола (в главном порядке байт); используйте этот элемент как аргумент protocol для socket.

Вы можете использовать getprotobyname и getprotobynumber, чтобы искать в базе данных протоколов специфический протокол. Информация возвращается в статически размещенной структуре; Вы должны копировать информацию, если Вы хотите сохранить ее для следующих обращений.

    struct protoent * getprotobyname (const char *name)  (функция)
Функция Getprotobyname возвращает информацию относительно сетевого протокола, именованного name. Если там нет такого протокола, она возвращает пустой указатель.

    struct protoent * getprotobynumber (int protocol)  (функция)
Getprotobynumber функция возвращает информацию относительно сетевого протокола с указанным номером. Если там нет такого протокола, она возвращает пустой указатель.

Вы можете также просматривать целую базу данных протоколов (по одному протоколу одновременно), используя setprotoent, getprotoent, и endprotoent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setprotoent (int stayopen)  (функция)
Эта функция открывает для просмотра базу данных протоколов.

Если аргумент stayopen является отличным от нуля, она устанавливает флаг так, чтобы последующие обращения к getprotobyname или getprotobynumber не закрыли базу данных. Это делается для большей эффективности, если Вы вызываете эти функции несколько раз, избегая повторного открытия базы данных для каждого обращения.

    struct protoent * getprotoent (void)  (функция)
Эта функция возвращает следующий вход в базе данных протоколов. Она возвращает пустой указатель, если не имеется больше входов.

    void endprotoent (void)  (функция)
Эта функция закрывает базу данных протоколов.

Пример Internet сокета.

Вот пример, показывающий, как создавать и называть сокет в именном пространстве Internet. Созданный сокет существует на машине, на которой выполняется программа. Вместо поиска и использования адреса Internet машины, этот пример определяет INADDR_ANY как главный адрес.

                #include <stdio.h>
                #include <stdlib.h>
                #include <sys/socket.h>
                #include <netinet/in.h>
                int
                make_socket (unsigned short int port)
                {
                        int sock;
                        struct sockaddr_in name;
                        sock = socket (PF_INET, SOCK_STREAM, 0);
                        if (sock < 0)
                                {
                                        perror ("socket");
                                        exit (EXIT_FAILURE);
                                }
                        name.sin_family = AF_INET;
                        name.sin_port = htons (port);
                        name.sin_addr.s_addr = htonl (INADDR_ANY);
                        if (bind (sock, (struct sockaddr *) &name,
                                                sizeof (name)) < 0)
                                {
                                        perror ("bind");
                                        exit (EXIT_FAILURE);
                                }
                        return sock;
                }
Вот другой пример, показывающий как Вы можете вносить в структуре sockaddr_in, данную строку главного имени и номер порта:

                #include <stdio.h>
                #include <stdlib.h>
                #include <sys/socket.h>
                #include <netinet/in.h>
                #include <netdb.h>
                void
                init_sockaddr (struct sockaddr_in *name, const
        char *hostname, unsigned short int port)
                {
                        struct hostent *hostinfo;
                        name->sin_family = AF_INET;
                        name->sin_port = htons (port);
                        hostinfo = gethostbyname (hostname);
                        if (hostinfo == NULL)
                                {
                                        fprintf (stderr, "Unknown host
                                                %s.\n", hostname);
                                        exit (EXIT_FAILURE);
                                }
                        name->sin_addr = *(struct in_addr *)
                        hostinfo->h_addr;
                }

11.6 Другие именные пространства

Конечно другие именные пространства и связанные семейства протоколов также реализованы, но не описаны здесь, потому что они редко используются. PF_NS обращается к протоколам Программного обеспечения Сети Ксерокса (Xerox Network Software). PF_ISO замещает Открытые системы Связи (Open Systems Interconnect). PF_CCITT обращается к протоколам из МККТТ (CCITT). "Socket.h" определяет эти символы и другие протоколы.

PF_IMPLINK используется для связи между главными ЭВМ и Процессорами Сообщений Internet.

11.7 Открытие и Закрытие сокетов

Этот раздел описывает фактические библиотечные функции для открытия и закрытия сокетов. Те же самые функции работают для всех именных пространств и стилей соединения.

Создание сокета.

Примитив для создания сокета - функция socket, объявлена в "sys/socket.h".

    int socket (int namespace, int style, int protocol)  (функция)
Эта функция создает сокет и определяет style стиль связи, который должен быть одним из стилей сокетов, перечисленных в Разделе 11.2 [Стили Связи]. Аргумент namespace определяет именное пространство; это должно быть PF_FILE (см. Раздел 11.4 [Именное пространство Файла]) или PF_INET (см. Раздел 11.5 [Именное пространство Internet]). protocol обозначает специфический протокол (см. Раздел 11.1 [Понятия Гнезда] ).

Возвращаемое значение из socket - описатель файла для нового сокета, или -1 в случае ошибки. Следующие errno условия ошибки определены для этой функции:

EPROTONOSUPPORT

Протокол или стиль не обеспечивается заданным именным пространством.

EMFILE процесс имеет слишком много открытых описателей файла.

ENFILE

система имеет слишком много открытыми описателей файла

EACCESS

процесс не имеет привилегии, чтобы создать сокет заданного стиля или протокола.

ENOBUFS

в системе закончилось внутреннее пространство буфера.

Описатель файла, возвращенный функцией socket поддерживает и чтение и запись. Но, подобно трубопроводам, сокеты не поддерживают операции позиционирования файла.

Пример вызова функции socket см. Раздел 11.4 [Именное пространство Файла].

Закрытие сокета.

Когда Вы закончили использование сокета, Вы можете просто закрыть описатель файла примитивом close; см. Раздел 8.1 [Открытие и Закрытие Файлов].

Вы можете также выключать только прием или только передачу на соединении, вызывая shutdown, которая объявлена в "sys/socket.h".

    int shutdown (int socket, int how)  (функция)
Функция shutdown выключает соединение с сокетом socket. Аргумент how определяет какое действие выполнить:

    0 Остановка при получения данных для этого сокета.
    1 Остановка при передаче данных с этого сокета.
    2 Остановка и приема и передачи.
Возвращаемое значение - 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:
EBADF

socket - не допустимый описатель файла.

ENOTSOCK

socket - не сокет.

ENOTCONN

socket не соединен.

Пары сокетов

Пара socket состоит из пары соединенных (но неименованных) сокетов. Это очень похоже на трубопровод и используется аналогичным способом. Пары сокетов создаются функцией socketpair, описание в файле "sys/socket.h".

    int socketpair (int namespace, int style, int protocol, int fields[Function2])
Эта функция создает пару сокетов, возвращая описатели файла в fields [0] и fields [1]. Пара сокетов - дуплексный канал связи, то есть и чтение и запись могут выполняться в любую сторону.

Аргументы namespace, style и protocol интерпретируется как в функции socket. style должен быть один из стилей связи, перечисленных в Разделе 11.2 [Стили связb]. Аргумент именного пространства определяет именное пространство, которое должно быть AF_FILE (см. Раздел 11.4 [Именное пространство Файла]); protocol определяет протокол связи.

Если style определяет стиль связи без установки логического соединения, то два сокета, которые Вы получаете, не соединены, строго говоря, но каждое из них знает другое как заданный по умолчанию адрес адресата, так что они могут посылать пакеты друг другу.

Функция Socketpair возвращает 0 при успехе и -1 при отказе. В переменной errno определяются следующие коды ошибок для этой функции:

EMFILE

Процесс имеет слишком много открытых описателей файла.

EAFNOSUPPORT

Не обеспечивается заданное именное пространство.

EPROTONOSUPPORT

Не обеспечивается заданный протокол.

EOPNOTSUPP

Заданный протокол не поддерживает создание пар сокетов.

11.8 Использование сокетов с соединениями.

Наиболее общие стили связи включают создание соединения с другим сокетом, и многократным обменом данными между этими сокетами. Создание соединения асимметрично; одна сторона (клиент) действует, чтобы запросить соединение, в то время как другая сторона (сервер) создает сокет и ждет запрос на соединение.

Создание Соединения

В создании соединения, клиент делает соединение, в то время как сервер ждет и принимает соединение. Здесь мы обсуждаем то, что клиентская программа должна делать, используя функцию connect, которая объявлена в "sys/socket.h".

    int connect (int socket, struct sockaddr *addr, size_t length) (функция)
Функция connect инициализирует соединение из сокета socket, чей адрес определен аргументами length и addr. (Этот сокет обычно находится на другой машине, и он должен быть установлен как сервер.) См. Раздел 11.3 [Адреса Сокетов], для уточнения инфрмации относительно того, как эти аргументы интерпретируются.

Обычно, connect ждет, пока сервер не отвечает на запрос прежде. Вы можете устанавливать режим неблокирования на сокете socket, чтобы заставить connect возвратиться немедленно без ожидания ответа. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения инфрмации относительно неблокирования.

Нормальное возвращаемое значение connect - 0. Если происходит ошибка, connect возвращает -1. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

сокет socket - не допустимый описатель файла.

ENOTSOCK

указанный сокет - не сокет.

EADDRNOTAVAIL

заданный адрес не доступен на отдаленной машине.

EAFNOSUPPORT

именное пространство addr не обеспечивается этим сокетом.

EISCONN

указанный сокет уже соединен.

ETIMEDOUT

попытка установить соединение не состоялась.

ECONNREFUSED

сервер активно отказался устанавливать соединение.

ENETUNREACH

сеть данного addr не доступна с этой главной ЭВМ.

EADDRINUSE

адрес сокета для данного addr уже используется.

EINPROGRESS

указанный сокет не-блокируемый, и соединение не могло бы быть установлено немедленно.

EALREADY

указанный сокет не-блокируемый и уже имеет отложенное соединение.

Ожидание Соединений

Теперь рассмотрим то, что процесс сервера должен делать, чтобы принять соединение из сокета. Это включает использование функции listen, чтобы дать возможность запросам на соединения через сокет, и позже использование функции accept (см. Раздел 11.8.3 [Принятие Соединений] ) чтобы действовать по запросу. Функция listen используется только для уже установленного логического соединения.

В именном пространстве Internet, не сущестует специальных механизмов защиты управления доступом к порту; любой процесс на любой машине может установить соединение с вашим сервером. Если Вы хотите ограничивать доступ к вашему серверу, заставьте его исследовать адреса, связанные с запросами соединения или выполнять некоторое другое подтверждение связи или протокол идентификации.

В именном пространстве Файла, обычные биты защиты файла управляют доступом к сокету.

    int listen (int socket, unsigned int n)  (функция)
Функция listen дает возможность указанному сокету воспринимать соединения, таким образом создается сокет сервера.

Аргумент n определяет длину очереди для отложенных соединений.

Функция listen возвращает 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

аргумент socket - не сокет.

EOPNOTSUPP

указанный сокет не поддерживает эту операцию.

Принятие Соединений

Когда сервер получает запрос соединения, он может создать соединение, принимая запрос. Для этих целей следует использовать функцию accept.

Сокет, который был установлен как сервер, может принимать запросы соединения от многих клиентов. Этот сокет сервера не станет частью соединения; взамен, accept делает новый сокет, который разделяет соединения. Accept возвращает описатель для этого сокета.

Исходный сокет сервера остается доступным для ожидания дальнейших запросов соединения.

Число отложенных запросов соединения на сокете сервера конечно. Если запросы соединения прибывают быстрее, чем сервер может их обработать, очередь может заполниться, и дополнительные запросы получат отказ с ошибкой ECONNREFUSED. Вы можете определять максимальную длину этой очереди как аргумент функции listen, хотя система может также наложить собственное внутреннее ограничение длины этой очереди.

    int accept (int socket, struct sockaddr *addr, size_t *length_ptr)
Эта функция используется для принятия запроса на соединения в указанном сокете сервера.

Функция accept находится в состоянии ожидания, когда нет возможности принять соединение, если, конечно, указанный сокет не имеет набор режимов неблокирования. (Вы можете использовать select, чтобы ждать отложенное соединение на неблокируемом сокете.) См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Аргументы Addr и length_ptr используется, чтобы возвратить информацию относительно имени клиентского сокета, которое инициализировало соединение. См. Раздел 11.3 [Адреса сокетов], для уточнения информации относительно формата.

Сокет, который был установлен как сервер не станет частью соединения; взамен, accept сделает новый сокет. Accept возвращает описатель для этого сокета. Нормальное возвращаемое значение accept - описатель файла для нового сокета.

После accept, первоначально указанный сокет остается открытым и не связанным, и продолжает ожидать, пока Вы не закрываете его. Вы можете принимать дальнейшие соединения с этим сокетом, вызывая accept снова.

Если происходит ошибка, и accept возвращает -1. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескрипторный аргумент socket - не сокет.

EOPNOTSUPP

описанный сокет не поддерживает эту операцию.

EWOULDBLOCK

сокет имеет набор режимов неблокирования, и нет никаких отложенных соединений.

Функцию accept не позволяется применять для сокета без установления логического соединения.

Кто соединен со Мной?

    int getpeername (int socket, struct sockaddr *addr, size_t *length_ptr)  (функция)
Функция Getpeername возвращает адрес сокета, с которым сокет соединен; она сохраняет адрес в пространстве памяти, заданном addr и length_ptr. Она сохраняет также длину адреса в *length_ptr.

См. Раздел 11.3 [Адреса Сокетов] , для уточнения информации относительно формата адреса. В некоторых операционных системах, getpeername работает только для сокетов в области Internet.

Возвращаемое значение - 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

указанный сокет - не сокет.

ENOTCONN

указанный сокет не соединен.

ENOBUFS

нет внутренних доступных буферов.

Пересылка Данных

Если сокет был соединен с равным, Вы можете использовать обычные примитивы read и write (см. Раздел 8.2[Примитивы ввода - вывода]), чтобы передать данные. Сокет - канал двусторонней связеи, так что чтение и запись может выполняться в оба конца.

Имеются также некоторые режимы ввода - вывода, которые являются специфическими для операций с сокетами. Чтобы определять эти режимы, Вы должны использовать функции recv и send вместо более обобщенного чтения и записи. Функции recv и send берут дополнительный аргумент, который Вы можете использовать, чтобы определить различные флаги, для управления специальными режимами ввода - вывода. Например, Вы можете определить флаг MSG_OOB, чтобы читать или писать внепоточные данные, а также флаги MSG_PEEK или MSG_DONTROUTE.

Посылка Данных

Функция send объявлена в файле "sys/socket.h". Если ваш аргумент flags нуль, Вы можете точно также использовать write вместо send. Если сокет был соединен, но соединение прервано, Вы получаете сигнал SIGPIPE для каждого использования send или write (см. Раздел 21.2.6 [Разнообразные Сигналы]).

    int send (int socket, void *buffer, size_t size, int flags) (функция)
Функция send - подобна write, но с дополнительными флагами Flags. Возможные значения flags описаны в Разделе 11.8.5.3 [Опции Данных сокетов].

Эта функция возвращает число переданных байтов, или -1 в противном случае. Если сокет неблокируемый, то send (подобно write) может возвращать после посылки только часть данных. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Обратите внимание, что успешное возвращаемое значение просто указывает, что сообщение было послано без ошибки, и не обязательно, что оно было получено без ошибки. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

EINTR

операция был прервана сигналом прежде, чем любые данные были посланы. См. Раздел 21.5 [Прерванные Примитивы].

ENOTSOCK

указанный сокет - не сокет.

EMSGSIZE

тип сокетаа требует, чтобы сообщение было послано быстро, но сообщение слишком большое для этого.

EWOULDBLOCK

на сокете был установле режим неблокирования, а операция записи блокирует. (Обычно send блокирует, пока операция не может быть завершена.)

ENOBUFS

не имеется достаточного внутреннего доступного пространства буфера.

ENOTCONN

Вы не соединили этот сокет.

EPIPE

Этот сокет был соединен, но соединение теперь разбито. В этом случае send генерирует SIGPIPE сначала; если этот сигнал игнорируется или блокируется, или если обработчик возвращается, то происходит сбой send с EPIPE.

Получение Данных

Функция recv объявлена в файле "sys/socket.h". Если ваш аргумент flags является нулем, Вы можете точно также использовать read вместо recv; см. Раздел 8.2 [Примитивы ввода-вывода].

    int recv (int socket, void *buffer, size_t size, int flags) (функция)
Функция recv подобна read, но с дополнительными флагами flags. Возможные значения flags описаны В Разделе 11.8.5.3 [Опции Данных сокетов].

Если режим неблокирования установлен для сокета, и никакие данные не доступны для чтения, recv не ожидает, а сразу возвращает код ошибки. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Эта функция возвращает число полученных байтов, или -1 в противном случае. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

EWOULDBLOCK

Режим неблокирования был установлен на сокете. (Обычно, recv блокирует пока не имеется входа, доступного для чтения.)

EINTR

операция была прервана сигналом прежде, чем любые данные прочитались. См. Раздел 21.5 [Прерванные Примитивы].

ENOTCONN

Вы не соединили этот сокет.

Опции Данных сокета.

Аргумент flags для send и recv - битовая маска. Вы можете объединить значения следующих макрокоманд вместе (через OR), чтобы получить значение для этого аргумента. Все они определены в файле "sys/socket.h".

    int MSG_OOB   (макрос)
Посылка или получение данных вне потока. См. Раздел 11.8.8 [Данные вне потока].

    int MSG_PEEK  (макрос)
Рассмотрение данных, но не удаление их из входной очереди. Это применимо только для функций типа recv (для send не подходят).

    int MSG_DONTROUTE  (макрос)
Не включать информацию о маршрутизации в сообщении. Это имеет смысл только с операциями вывода, и обычно представляет интерес только для диагностики программы.

Пример сокета с потоком байтов.

Вот пример программы клиента, которая устанавливает соединение для сокета в пространстве Internet с поточным типом передачи данных. Она не делает ни чего особенно интересного; если она соединилась с сервером, она посылает текстовую строку серверу и выходит.

                #include <stdio.h>
                #include <errno.h>
                #include <stdlib.h>
        #include <unistd.h>
                #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <netdb.h>
        #define PORT    5555
        #define MESSAGE "Yow!!! Are we having fun yet?!?"
        #define SERVERHOST              "churchy.gnu.ai.mit.edu"
        void
        write_to_server (int filedes)
        {
                int nbytes;
                nbytes=write(filedes,MESSAGE,strlen(MESSAGE)+1);
                if (nbytes < 0)
                        {
                                perror ("write");
                                exit (EXIT_FAILURE);
                        }
        }
        int
        main (void)
        {
                extern void init_sockaddr(struct sockaddr_in*name,
                                                const char *hostname,
                                                unsigned short int port);
                int sock;
                struct sockaddr_in servername;
                sock = socket (PF_INET, SOCK_STREAM, 0);
                if (sock < 0)
                        {
                                perror ("socket (client)");
                                exit (EXIT_FAILURE);
                        }
                init_sockaddr (&servername, SERVERHOST, PORT);
                if (0 > connect (sock,
                                        (struct sockaddr *) &servername,
                                        sizeof (servername)))
                        {
                                perror ("connect (client)");
                                exit (EXIT_FAILURE);
                        }
                write_to_server (sock);
                close (sock);
                exit (EXIT_SUCCESS);
        }

Пример соединения сервера (Тип соединения - поток байтов)

Текст программы сервера намного более сложен. Так как мы хотим предоставлять многим клиентам быть соединенными с сервером, но в то же самое время, было бы неправильно ждать ввод от одиночного клиента, просто вызывая read или recv. Взамен, нужно использовать select (см. Раздел 8.6 [Ждущий ввод - вывод] ), чтобы ждать ввод на всех открытых сокетах. Это также позволяет серверу иметь дело с дополнительными запросами соединения.

Этот специфический сервер не делает хоть что-нибудь интересное, если он получил сообщение от клиента, то он закрывает сокет клиента по получению признака конца файла.

Эта программа использует make_socket и init_sockaddr для устанавления адреса сокета; см. раздел 11.5.7 [Inet Пример].

        #include <stdio.h>
        #include <errno.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <netdb.h>
        #define PORT   5555
        #define MAXMSG  512
        int
        read_from_client (int filedes)
        {
                char buffer[MAXMSG];
                int nbytes;
                nbytes = read (filedes,buffer,MAXMSG); 
                if (nbytes < 0)
                        {
                                perror ("read");
                                exit (EXIT_FAILURE);
                        }
                else if (nbytes == 0)
                        return -1;
                else
                        {
                                fprintf (stderr, "Server: got message:  
                                        `%s'\n", buffer);
                                        return 0;
                        }
        }
        int
        main (void)
        {
                extern int make_socket (unsigned short int port);
                int sock;
                int status;
                fd_set active_fd_set, read_fd_set;
                int i;
                struct sockaddr_in clientname;
                size_t size;
                sock = make_socket (PORT);
                if (listen (sock, 1) < 0)
                {
                        perror ("listen");
                        exit (EXIT_FAILURE);
                }
                FD_ZERO (&active_fd_set);
                FD_SET (sock, &active_fd_set);
                while (1)
                        {
                                if (select (FD_SETSIZE,
                                        &read_fd_set, NULL,
                                        NULL, NULL) < 0)
                                {
                                        perror ("select");
                                        exit (EXIT_FAILURE);
                                }
                        if (FD_ISSET (i, &read_fd_set))
                        {
                                if (i == sock)
                                {
                                        if (accept (sock,
                        (struct sockaddr *) &clientname, &size) < 0)
                                                                                        
                                {
                                                perror ("accept");
                                                exit (EXIT_FAILURE);
                                        }
                                        fprintf (stderr,
                "Server: connect from host %s, port %hd.\n",
                                        inet_ntoa (clientname.sin_addr),
                                        ntohs (clientname.sin_port));
                                        FD_SET (status, &active_fd_set);
                                }
                                else
                                {
                                        if (read_from_client (i)<0)
                                        {
                                                close (i);
                                                FD_CLR (i, &active_fd_set);
                                        }
                                }
                        }
                }
        }

Данные Вне потока

Потоки с соединениями разрешающими данные вне потока имеют приоритет выше, чем обычные данные. Обычно причина для посылки данных вне потока - исключительные условия. Способ послать данные вне потока использует send с флагом MSG_OOB (см. Раздел 11.8.5.1 [Посылка Данных]).

Данные вне потока посылаются с высшим приоритетом, плюс процесс получения не обрабатывает их в обыкновенное очереди, но чтобы читать доступные данные вне потока следует использовать recv с флагом MSG_OOB (см. Раздел 11.8.5.2 [Получение Данных]). Обычные операции чтения не воспринимают данные вне потока; они читают только обычные данные.

Когда сокет находит, что данные вне потока продвигаются, он посылает сигнал SIGURG процессу владельца или группе процессов сокета. Вы можете определять владельца, используя команду F_SETOWN для функции fcntl; см. Раздел 8.12 [Ввод Прерывания]. Вы должны также установить обработчик для этого сигнала, как описано в Главе 21 [Обработка Сигналов], для соответствующего действия типа чтения данных вне потока.

В качестве альтернативы, Вы можете проверять задержать данные вне потока, или ждать данные вне потока, при использовании функции select; она может ждать исключительное условие на гнезде. См. Раздел 8.6 [Ждущий ввод - вывод].

Уведомление о данных вне потока (с SIGURG или с select) обозначает, что данные вне потока находятся в пути; данные не могут фактически прибывать позже. Если Вы пробуете читать данные вне потока прежде, чем они ппребывают, то recv генерирует ошибку с кодом EWOULDBLOCK.

Посылка таких данных автоматически помещает "метку" в потоке обычных данных, показывающую, где в последовательности данных " были бы "данные вне потока. Это полезно, когда значение данных вне потока - " отменяет все посланное до ". Вот, как Вы можете в процессе получения проверять, были ли любые обычные данные посланы перед меткой:

    success = ioctl (socket, SIOCATMARK, &result);
Имеется функция, чтобы отбросить любые обычные данные, предшествующие данным вне потока:
                int
                discard_until_mark (int socket)
                {
                        while (1)
                        {
                                char buffer[1024];
                                int result, success;
                                success = ioctl (socket, SIOCATMARK,
                                &result); if (success < 0)
                                perror ("ioctl");
                                if (result)
                                        return;
                                success = read (socket, buffer, sizeof
                                                buffer);
                                if (success < 0)
                                perror ("read");
                        }
                }
Если Вы не хотите отбрасывать обычные данные, предшествующие метке, Вам необходимо создать место во внутренних буферах систем для данных вне потока. Если Вы пробуете читать данные вне потока и получаете ошибку EWOULDBLOCK, попробуйте читать некоторые обычные данные (сохраняя их так, чтобы Вы могли использовать их позже) и смотрите появится ли необходимое место. Вот пример:
                struct buffer
                {
                        char *buffer;
                        int size;
                        struct buffer *next;
                };
        struct buffer *
        read_oob (int socket)
        {
                struct buffer *tail = 0;
                struct buffer *list = 0;
                while (1)
                {
                        char *buffer = (char *) xmalloc (1024);
                        struct buffer *link;
                        int success;
                        int result;
                        success = recv (socket, buffer, sizeof
                buffer, MSG_OOB);
                        if (success >= 0)
                        {
                                link->size = success;
                                link->next = list;
                                return link;
                        }
                        success = ioctl (socket, SIOCATMARK,
                &result);
                         if (success < 0)
                                perror ("ioctl");
                        if (result)
                        {
                                sleep (1);
                                continue;
                        }
                        success = read (socket, buffer, sizeof
                        buffer);
                        if (success < 0)
                        perror ("read");
                        {
                                link->size = success;
                                if (tail)
                                        tail->next = link;
                                else
                                        list = link;
                                tail = link;
                        }
                }
        }

11.9 Датаграмные операции сокета

Этот раздел описывает, как использовать стили связи, которые не используют соединения (стили SOCK DGRAM и SOCK_RDM). При использовании этих стилей, Вы группируете данные в пакеты, и каждый пакет - независимая связь. Вы определяете адресата для каждого пакета индивидуально.

Датаграмные пакеты подобны письмам: Вы посылаете каждый независимо, с собственным адресом адресата, и они могут прибывать в неправильном порядке или вообще не прибывать.

Функции listen и accept не предназначены для сокетов, использующих стили связи без установки логического соединения.

Посылка Датаграмм

Нормальный способ посылки данных относительно датаграмного сокета использует функцию sendto, объявленную в "sys/socket.h".

!!! Вы можете вызывать connect на датаграмном сокете, но эта функция определяет заданного по умолчанию адресата для дальнейшей пе­ редачи данных на сокете. Когда сокет имеет по умолчанию заданного ад­ ресата, Вы можете использовать send (см. Раздел 11.8.5.1 [Посылка Дан­ ных] ) или write (см. Раздел 8.2 [Примитивы ввода - вывода] ) для по­ сылки пакетов. Вы можете отменять заданного по умолчанию адресата, вы­ зывая connect, и используя формат адреса AF_UNSPEC в аргументе addr. См. Раздел 11.8.1 [Соединение].

    int sendto (int socket, void *buffer. size_t size, int flags, sockaddr *addr, size_t length)
Эта функция пересылает данные из buffer через сокет socket по заданному адресу. size задает число пересылаемых байт.

Flags интерпретируется также как и в send; см. Раздел 11.8.5.3 [Опции данных сокетов].

Возвращаемое значение и условия ошибок такие же как и для send, но Вы не можете полагаться на систему для обнаружения ошибок и сообщения о них; наиболее общая ошибка состоит в том, что пакет потеряется или не имеется никого в заданном адресе, чтобы получить его, и операционная система на вашей машине обычно не знает этого.

Также возможно, что для одного обращения к sendto она сообщит ошибку из-за проблемы связанной с предыдущим обращением.

Получение Датаграмм

Функция recvfrom читает пакет из датаграмного сокета и также сообщает Вам, откуда он был послан. Эта функция объявлена в "sys/socket.h".

    int recvfrom (int socket, void *buffer, size_t size, int flags, struct sockaddr *addr, size_t *length_ptr)
Функция recvfrom читает один пакет из указанного сокета в указанный буфер.

Аргумент size определяет максимальное число байтов, которые нужно читать.

Если пакет является больше чем size байт, то, Вы получаете первые size байт пакета, а остальная часть пакета потеряна. Не существует способа прочитать остальную часть пакета. Таким образом, когда Вы используете протокол пакетов, Вы должны всегда знать длину ожидаемого пакета.

Аргументы addr и length_ptr используются для возвращения адреса источника пакета. См. Раздел 11.3 [Адреса сокетов]. Для сокета в области файла, информация адреса не будет значима, так как Вы не можете читать адрес такого сокета (см. Раздел 11.4 [Именное пространство Файла] ). Вы можете определять пустой указатель как аргумент addr, если Вы не заинтересованы в этой информации.

Flags интерпретируется тем же самым способом как recv (см. Раздел 11.8.5.3 [Опции Данных сокета]). Возвращаемое значение и условия ошибки - такие же как для recv.

Вы можете использовать recv (см. Раздел 11.8.5.2 [Получение Данных]) вместо recvfrom, если знаете, что не должны выяснить, кто послал пакет. Даже read, может использоваться, если Вы не хотите определять flags (см. Раздел 8.2 [Примитивы ввода ­ вывода]).

Датаграмный Пример сокета.

Вот набор примеров программ, которые посылают сообщения используя датаграмный стиль. И клиент и сервер используют функцию make_named_socket, которая была предоставлена в Разделе 11.4 [Именное пространство Файла], для создания и связывания сокетов.

Сначала программа сервера. Очевидно, это не особенно полезная программа, но она показывает общие идеи.

                #include <stdio.h>
                #include <errno.h>
                #include <stdlib.h>
                #include <sys/socket.h>
                #include <sys/un.h>
                #define SERVER  "/tmp/serversocket"
                #define MAXMSG  512
                int
                main (void)
                {
                        int sock;
                        char message[MAXMSG];
                        struct sockaddr_un name;
                        size_t size;
                        int nbytes;
                        sock = make_named_socket (SERVER);
                        while (1)
                        {
                                size = sizeof (name);
                                nbytes=recvfrom(sock,message,MAXMSG,0,
                                        (struct sockaddr*) & name,&size);
                                if (nbytes < 0)
                                {
                                        perror ("recfrom (server)");
                                        exit (EXIT_FAILURE);
                                }
                                fprintf (stderr, "Server: got message:
                                %s\n", message);
                                nbytes=sendto(sock,message,nbytes,0,
                                        (struct sockaddr*) & name,size);
                                if (nbytes < 0)
                                {
                                        perror ("sendto (server)");
                                        exit (EXIT_FAILURE);
                                }
                        }
                        }

Пример Чтения Датаграмм

Вот программа клиента, соответствующая серверу выше.

Она посылает датаграмму серверу и ждет ответ. Обратите внимание, что сокету клиента (также как для сервера) в этом примере должно быть дано имя. Так, чтобы сервер мог направлять сообщение обратно клиенту. Так как сокет не имеет никакого связанного состояния соединения, единственый способ, которым сервер может сделать это ссылаясь на имя клиента.

                #include <stdio.h>
                #include <errno.h>
                #include <unistd.h>
                #include <stdlib.h>
                #include <sys/socket.h>
                #include <sys/un.h>
                #define SERVER  "/tmp/serversocket"
                #define CLIENT  "/tmp/mysocket"
                #define MAXMSG  512
                #define MESSAGE "Yow!!! Are we having fun yet?!?"
                int
                main (void)
                {
                        extern int make_named_socket (const
                char *name);
                        int sock;
                        char message[MAXMSG];
                        struct sockaddr_un name;
                        size_t size;
                        int nbytes;
                        sock = make_named_socket (CLIENT);
                        name.sun_family = AF_UNIX;
                        strcpy (name.sun_path, SERVER);
                        size = strlen (name.sun_path) + sizeof (name.sun_family);
                        nbytes = sendto (sock, MESSAGE,
        strlen (MESSAGE) + 1, 0, (struct sockaddr *) & name, size);
                        if (nbytes < 0)
                        {
                                perror ("sendto (client)");
                                exit (EXIT_FAILURE);
                        }
                nbytes =recvfrom(sock,message,MAXMSG,0,NULL,0);
                if (nbytes < 0)
                {
                        perror ("recfrom (client)");
                        exit (EXIT_FAILURE);
                }
                fprintf (stderr,"Client: got message: %s\n",message);
                        remove (CLIENT);
                        close (sock);
                }
Имейте в виду, что датаграмная связь сокетов ненадежна. В этом примере программа клиента ждет неопределенное время, если сообщение никогда не достигает сервера, или, если ответ сервера никогда не возвращается. Более автоматическое решение могло бы использовать select (см. Раздел 8.6 [Ждущий ввод - вывод]), чтобы установить период блокировки по времени для ответа, и в этом случае или снова послать сообщение, или выключить сокет и выйти.

11.10 Демон Inetd

Мы объяснили выше, как написать программу сервера, которая реализует собственное ожидание. Такой сервер должен уже выполниться для любого соединения с ним.

Другой способ обеспечивать обслуживание портов для Internet состоит в том, чтобы использовать в программе для ожидания демона inetd. Inetd - программа, которая выполняется все время и ждет (используя select) сообщения на заданном наборе портов. Когда она получает сообщение, она принимает соединение (если стиль сокета запрашивает соединение), и тогда запускает дочерний процесс, чтобы выполнить соответствующую программу сервера. Вы определяете порты и их программы в файле "/etc/inetd.conf".

Inetd Серверы

Написание программы сервера, которая будет выполнена inetd очень просто. Каждый раз когда кто-то запрашивает соединение с соответствующим портом, стартует новый процесс сервера. Соединение уже существует в это время; гнездо доступно как описатель стандартного ввода и как описатель стандартного вывода (описатели 0 и 1) в процессе сервера. Так что программа сервера может начинать читать и писать данные сразу же. Часто программа нуждается только в обычных средствах ввода-вывода; фактически, универсальная программа-фильтр, которая не знает ничего относительно сокетов, может работать как сервер потока байтов, запускаемая inetd.

Вы можете также использовать inetd для серверов, которые используют стили связи без установления логического соединения. Для этих серверов, inetd не пробует принять соединение, так как никакое соединение не возможно. Она только начинает программу сервера, которая может читать входящий датаграмный пакет из описателя 0. Программа сервера может обрабатывать один запрос и выходить, или читать большое количество запросов. Вы должны определить, который из этих двух методов использования сервера удобен Вам при конфигурации inetd.

Конфигурирование inetd

Файл "/etc/inetd.conf " сообщает inetd, какие порты ожидает какой сервер для обработки пакетов. Обычно каждый вход в файле - это строка, но Вы можете разбивать его на много строк, если все, кроме первой строки входа, начинаются с пропуска. Строки, которые начинаются с "*" являются комментариями.

Имеются два стандартных входа в "/etc/inetd.conf":

    ftp stream tcp nowait root /libexec/ftpd ftpd
    talk dgram udp wait root /libexec/talkd talkd
Вход имеет формат:

    service style protocol wait username program arguments
Поле service говорит, какое обслуживание обеспечивает эта программа. Это должно быть имя обслуживания, определенного в "/etc/services". Inetd использует обслуживание, чтобы решить какой порт слушать для этого входа.

style и protocol определяют стиль связи и протокол для использования ожидающего сокета. Стиль должен иметь имя стиля связи, преобразованного в строчные буквы и с удаленным "SOCK_", например, "stream" или "dgram". Протокол должен быть один из протоколов, перечисленных в "/etc/protocols". Типичные имена протокола - "tcp" для соединений потока байтов и "udp" для ненадежных датаграмм.

Поле wait должно быть, или "wait" или "nowait". "wait" используется, если стиль не требует установления логического соединения и обрабатывает многократные запросы. "nowait" используется, когда необходимо,чтобы inetd начинал новый процесс для каждого сообщения, или запроса, который приходит. Если используется соединение, то wait должен быть "nowait".

user - имя пользователя, под которым сервер должен выполняться. Inetd выполняется под пользователя root, так что она может устанавливать ID пользователей дочерних процессов произвольно. Лучше избегать использования "root" для пользователя; но некоторые серверы, типа Telnet и FTP, читают username и пароль самостоятельно. Эти серверы должны быть запущены под пользователя root изначально, так как они могут регистрировать потоки данных передаваемых по сети.

program вместе с аргументами определяет команду, для запуска сервера. Это должно быть абсолютное имя файла, определяющее исполняемый файл для выполнения. Аргументы состоят из любого числа отделенных пробелами слов, которые станут аргументами командной строки программы.

Первое слово в аргументах - нуль, который должен быть именем программы непосредственно (каталоги sans).

Если Вы редактируете "/etc/inetd.conf", то Вы можете указывать необходимость повторного чтения файла для inetd и сообщения нового содержимого, посылая inetd сигнал SIGHUP. Вы будете должны использовать ps, чтобы определить ID процесса inetd, поскольку оно не фиксировано.

11.11 Опции сокетов.

Этот раздел описывает, как читать или установить различные опции, которые изменяют поведение сокетов и их основных протоколов связи.

Когда Вы манипулируете опциями сокета, Вы должны определить, к какому уровню они относятся, то есть применяется ли опция к интерфейсу сокета, или к интерфейсу протокола связи низшего уровня.

Функции Опций сокета.

Имеются функции для исследования и изменения опций сокета. Они объявлены в "sys/socket.h".

       int getsockopt (int socket, int level, int optname, void *optval, size_t *optlen_ptr)
Функция getsockopt получает информацию относительно значения опции optname заданного уровня для указанного сокета.

Значение опции сохранено в буфере, на который указывает optval. Перед обращением, Вы должны обеспечить в * optlen_ptr размер этого буфера; по возвращении, он содержит число байтов информации, фактически сохраненной в буфере.

Большинство опций интерпретирует буфер optval как одиночное значение int.

Фактически возвращаемое значение getsockopt - 0 при успехе и -1 в случае неудачи. В переменной errno отражены следующие возможные причины:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

ENOPROTOOPT

Optname не имеет смысла для данного уровня.

    int setsockopt (int socket, int level, int optname, void *optval, size_t optlen)
Эта функция используется, чтобы установить опцию сокета optname заданного уровня для указанное сокета. Значение опции передано в буфере optval, который имеет размер optlen.

Возвращаемое значение и коды ошибки для setsockopt такие же как для getsockopt.

Опции уровня сокета.

    int SOL_SOCKET  (константа)
Используйте эту константу, как аргумент level для getsockopt или setsockopt, чтобы манипулировать опциями уровня сокета, описанными в этом разделе.

Вот таблица имен опций уровня сокета; все они определены в файле "sys/socket.h".

    SO_DEBUG
Эта опция переключает запись информации об отладке в основных модулях протокола. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_REUSEADDR
Эта опция говорит bind (см. Раздел 11.3.2 [Установка Адреса]) разрешить многократное использование местных адресов для этого сокета. Если Вы пользуетесь этой опцией, Вы можете фактически иметь два сокета с тем же самым номером порта Internet. Необходимость в этой опции возникает, потому что что некоторые протоколы Internet с более высоким уровнем, такие FTP, требуют, чтобы Вы многократно использовали тот же самый номер сокета.

Значение имеет тип int; значение отличное от нуля означает "да".

    SO_KEEPALIVE
Эта опция указывает, должен ли основной протокол периодически передавать сообщения на соединенный сокет. Если адресат будет не в состоянии отвечать на эти сообщения, соединение рассматривается разорваным. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_DONTROUTE
Эта опция контролирует при посылке сообщения обход нормальных средств посылки сообщений. Если она установлена, сообщения посылаются непосредственно сетевому интерфейсу. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_LINGER
Эта опция определяет то, что должно случиться, в случае, когда сокет предоставляющий надежную выдачу все еще не передал сообщения, до закрытия; см. Раздел 11.7.2 [Закрытие сокета]. Значение имеет тип struct linger.

    struct linger  (тип данных)
Эта структура имеет следующие элементы:

    int l_onoff
Это поле интерпретируется как булевское. Оно отлично от нуля, если блокировка закрыта, пока данные не переданы, или период блокировки по времени не истек.

    int l_linger
Определяет период блокировки по времени, в секундах.

    SO_BROADCAST
Эта опция определяет могут ли датаграммы быть широковещательно переданы из сокета. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_OOBINLINE
Если эта опция установлена, данные вне потока получаемые в сокет помещаются в нормальную входную очередь. Она разрешает читать их, используя read или recv без того, чтобы определить флаг MSG_OOB. См. Раздел 11.8.8 [Данные вне потока]. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_SNDBUF
Эта опция получает или устанавливает размер буфера вывода. Значение size_t является его размером в байтах.

    SO_RCVBUF
Эта опция получает или устанавливает размер буфера ввода. Значение size_t является его размером в байтах.

    SO_STYLE
    SO_TYPE
Эта опция может использоваться только с getsockopt. Она используется, чтобы получить стиль связи сокета. SO_TYPE - историческое имя, а SO_STYLE - привилегированное имя в GNU. Значение имеет тип int, и обозначает стиль связи; см. Раздел 11.2 [Стили Связи].

    SO_ERROR
Эта опция может использоваться только с getsockopt. Она используется, чтобы сбросить состояние ошибки сокета. Значение int представляет собой предыдущее состояние ошибки.

11.12 База данных Сетей

Много систем приходят с базой данных, которая записывает список сетей, известных разработчику системы. Она обычно сохраняется или в файле "/etc/networks" или в блоке преобразования имен. Эта база данных полезна для маршрутизации программ типа route, но бесполезна для программ, которые просто связываются по сети. Функции предназначенные для обращения к этой базе данных описаны в "netdb.h ".

    struct netent                 (тип данных)
Этот тип данных используется, чтобы представить информацию относительно входов в базе данных сетей.

Он имеет следующие элементы:

    char *n_name
Это - "официальное" имя сети.

    char **n_aliases
Это альтернативные имена для сети, представляемые как вектор строк. Пустой указатель завершает массив.

    int n_addrtype
Это - тип сетевого номера; он всегда равно AF_INET для сетей Internet.

    unsigned long int n_net
Это сетевой номер. Сетевые числа представлены в главном порядке байтов; см. Раздел 11.5.5 [Порядок Байтов].

    struct netent * getnetbyname (const char *name)     (функция)
Getnetbyname функция возвращает информацию относительно сети именованной name. Она возвращает пустой указатель, если нет никакой сети.

    struct netent * getnetbyaddr (long net, int type)   (функция)
Функция getnetbyaddr возвращает информацию относительно сети указанного типа с номером net. Вы должны определить значение AF_INET для аргумента type для сети Internet.

    getnetbyaddr возвращает пустой указатель в случае отсутствия такой сети.
Вы можете также просматривать базу данных сетей, используя setnetent, getnetent, и endnetent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setnetent (int stayopen)
Эта функция открывает базу данных сетей.

Если аргумент stayopen является отличным от нуля, то она устанавливает флаг так, чтобы последующие обращения к getnetbyname или getnetbyaddr не закрыли базу данных. Это делается для большей эффективности, если Вы вызываете эти функции несколько раз, избегая повторного открытия базы данных для каждого обращения.

    struct netent * getnetent (void)
Эта функция возвращает следующий вход в базе данных сетей. Она возвращает пустой указатель, если не имеется больше входов.

    void endnetent (void)
Эта функция закрывает базу данных сетей.


Вперед Назад Содержание


Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру