Обобщенная структура адреса
Обобщенный формат оконечной точки состоит из пары значений: (address_family, endpoint_address). Поле address_famly (семейство адресов) содержит константу, которая определяет один из заранее назначенных типов адресов, а поле endpoint_address (адрес оконечной точки) содержит адрес оконечной точки, в котором используется стандартное представление для указанного типа адреса.
На практике программное обеспечение сокетов содержит объявления заранее определенных структур С для адреса оконечных точек. Наиболее общая структура известна под названием sockaddr. Она содержит 2-байтовый идентификатор семейства адресов и 14-байтовый массив для хранения адреса:
struct sockaddr
{
u_char sa_len; /* Общая длина */
u_short sa_family; /* Тип адреса */
char sa_data[14]; /* Значение адреса */
}
В некоторых системах UNIX предусмотрена возможность определять в семействе адресов AF UNIX программную конструкцию, называемую именованным каналом. Адреса оконечной точки в семействе AF_UNIX состоят из имени пути в файловой системе, которое может намного превышать по длине 14 байт. Поэтому в прикладных программах нельзя использовать структуру sockaddr при объявлении переменных, поскольку она не имеет достаточной длины для хранения всех возможных адресов оконечных точек.
Замечание: На практике часто возникает путаница, поскольку структура sockaddr как раз и предназначена для хранения адресов семейства AF_INET. Поэтому программное обеспечение TCP/IP работает правильно, даже если программист объявляет переменные как относящиеся к типу sockaddr. Однако для обеспечения переносимости и удобства сопровождения программ в коде работы с протоколами TCP/IP не следует использовать в объявлениях структуру sockaddr.
В каждом семействе протоколов определено точное представление адресов оконечных точек, а в ПО сокетов предусмотрены соответствующие объявления структур. Каждый адрес оконечной точки TCP/IP состоит из 2-байтового поля, которое обозначает тип адреса (константа AF_INET), 2-байтового поля номера порта, 4-байтового поля IP-адреса и 8-байтового поля, которое зарезервированно. Этот формат определен в структуре sockaddr_in:
struct sockaddr_in
{
u_char sin_len; /* Общая длина */
u_short sin_family; /« тип адреса */
u_short sin_port; /* Номер порта протокола */
struct in_addr sin_addr; /* IP-адрес */
char sin_zero[8]; /* Не используется */
}
В приложении, в котором используются исключительно только протоколы TCP/IP, следует применять структуру sockaddr_in.
Основные системные вызовы в api-интерфейсе сокетов
Функция socket
В приложении функция socket вызывается для создания нового сокета, который может использоваться для сетевой связи. После вызова эта функция возвращает дескриптор вновь созданного сокета. Параметры вызова обозначают семейство протоколов, которое будет применяться в приложении (например, PF_INET для TCP/IP), и необходимый протокол или тип службы (например, потоковый или дейтаграммный).
Пример: sd = socket(PF_INET,SOCK_STREAM,0)
Функция connect
После создания сокета клиент вызывает функцию connect для установления активного соединения с удаленным сервером. Параметр функции connect, который включает IP-адрес удаленного компьютера и номер порта протокола, позволяет клиенту указать удаленную оконечную точку. После установления соединения клиент может передавать с его помощью данные.
Пример: connect(sd,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
Функция send
Функция send используется и в клиентах, и в серверах для передачи данных через соединение TCP. Имеет четыре параметра: дескриптор сокета, в который должны быть отправлены данные, адрес передаваемых данных, их длину и флаги. Обычно функция send копирует исходящие данные в буферы, находящиеся в ядре операционной системы, и позволяет приложению продолжать выполнение, в то время как происходит передача данных по сети. Если буферы системы переполняются, вызов функции send может быть временно заблокирован, до тех пор, пока не появится возможность передачи по протоколу TCP данных по сети. Возвращает кол-во посланных байт.
Функция recv
Для чтения данных из соединения приложение вызывает функцию recv с четырьмя параметрами. Первый параметр задает используемый дескриптор сокета. второй определяет адрес буфера, а третий указывает длину буфера. Четвертым параметром являются флаги. Функция recv извлекает байты данных, поступающие через сокет, и копирует их в область буфера, определяемую пользователем. Если данные не поступают, вызов функции recv блокируется до тех пор, пока они не начнут поступать. Если поступает больше данных, чем помещается в буфере, функция recv извлекает из сокета только тот объем, который является достаточным для заполнения буфера. Возвращает число полученных байт.
При вызове функции для UDP-сокета извлекается одно входящее сообщение UDP (одна пользовательская дейтаграмма). Если буфер не может вместить полностью все сообщение, функция recv заполняет буфер и отбрасывает оставшиеся данные.
Замечание: В системе Linux можно использовать функцию read вместо recv, и write вместо send.
Функция close
После завершения использования сокета в клиентской или серверной программе необходимо вызвать функцию close для его освобождения. Если сокет используется только одним процессом, функция close немедленно разрывает соединение и освобождает сокет. Если сокет находится в совместном использовании нескольких процессов, функция close уменьшает число ссылок на сокет и освобождает его, как только число ссылок достигает нуля.
Функции bind
Используется для указания адреса локальной оконечной точки. Этот вызов принимает параметры, в которых задаются дескриптор сокета и адрес оконечной точки.
Замечание: В сервере функция bind используется в основном для указания общепринятого номера порта. В клиенте номер порта не указывается, и эта функция как правило не используется. ОС самостоятельно назначает клиентскому приложению оконечную точку при вызове функции connect.
Пример: bind(sd,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
Функция listen
Сразу после создания сокет не является ни активным (клиентом), ни пассивным (сервером). Серверы с установлением логического соединения вызывают функцию listen, чтобы перевести сокет в пассивный режим и подготовить его для приема входящих запросов на установление соединения.
Большая часть серверных программ состоит из бесконечного цикла, в котором принимается очередной входящий запрос на установление соединения, выполняется его обработка, а затем происходит возврат к выполнению операции приема очередного входящего соединения. Даже если обработка каждого входящего соединения занимает только несколько миллисекунд, может оказаться, что новый запрос на установление соединения поступит в тот момент, когда сервер занимается обработкой текущего запроса. Для предотвращения этого функции передается параметр, который указывает длину очереди запросов на установление соединения.
Пример: listen(sd,10);
Функция accept
При работе с сокетами TCP в серверной программе после вызова функций создания сокета (socket), указания адреса локальной оконечной точки (bind) и перевода сокета в пассивный режим (listen) вызывается функция accept для извлечения из очереди следующего входящего запроса на установление соединения. Параметр функции указывает сокет, из очереди которого должен быть принят запрос на соединение.
Функция создает новый сокет для каждого нового запроса на соединение и возвращает дескриптор нового сокета в вызывающий оператор. В сервере новый сокет используется только для нового соединения, а первоначальный сокет служит для приема следующих запросов на соединение. Сразу после приема запроса на соединение сервер может передавать данные через новый сокет. Возвращает также адрес оконечной точки отправления принятого запроса.
Пример: news = accept(sd, (struct sockaddr*)&sock_addr,&s_len)
См. также
Д-р. Сидни Фейт. TCP/IP Архитектура, протоколы, реализация (стр.334-351)
Теренс Чан . Системное программирование на C++ для UNIX (364-373)
