
- •3. Возникают некоторые проблемы
- •2. Ввод-вывод сетевых данных и данных в файловой системе
- •3. Возникают некоторые проблемы
- •4. Абстракция сокетов
- •5. Отличие дескриптора сокета от дескриптора файла
- •6. Создание сокета
- •7. Параметры сокета
- •8. Семейства протоколов и адресов
- •9. Тип соединения
- •10. Выбор протокола
- •11. Процесс
- •12. Что такое дескриптор сокета?
- •14. Использование сокета в программе
- •15. Настройка сокета
- •15. Соединение сокета
- •16. Указание локального адреса (порта протокола)
- •17. Передача данных через сокет
- •18. Передача данных через соединенный сокет
- •19. Передача данных через не соединенный сокет
- •20. Прием данных через сокет
- •22. Сокеты и серверы
- •23. Функция listen
- •24. Функция accept
- •25. Процесс-сервер
- •26. Принципы проектирования параллельного сервера
- •28. Подводя итоги
19. Передача данных через не соединенный сокет
Чтобы послать данные через соединенный сокет, то есть сокет протокола, ориентированного на соединение, прикладная программа может использовать одну из трех описанных в предыдущем разделе функций. Однако ни одна из них не позволяет указывать адрес получателя данных. Для того чтобы послать данные через не соединенный сокет, требуется вызвать одну из двух следующих функций: sendto или sendmsg. Они обеспечиваются интерфейсом сокетов именно для этих целей. Функция sendto требует шесть параметров в качестве аргументов. Первые четыре те же, что и в функции send. Пятый параметр, структура .адреса сокета, определяет адрес назначения. Шестой параметр, длина структуры адреса сокета, — размер этой структуры в байтах. Следующий оператор демонстрирует вызов функции sendto:
result = sendto(socket_handle, message_buffer, buffe.r_length,
special_f lags, socket_address_structure, address_structure_length);
Функция sendmsg позволяет использовать гибкую структуру данных вместо буфера, расположенного в непрерывной области памяти. Следующий оператор демонстрирует вызов sendmsg. В качестве аргументов указываются дескриптор сокета, указатель на структуру данных и дополнительные флаги:
result = sendmsg (socket_handle, message_structure, special_flags )
Структура сообщения позволяет программе гибко размещать длинные списки параметров сообщения в единой структуре данных. Функция sendmsg похожа на writev в том, что прикладная программа может разместить свои данные в нескольких раздельно расположенных блоках памяти. Другими словами, как и в функции writev, структура сообщения содержит указатель на массив адресов памяти. На рис. 3 показан пример формата структуры сообщения, используемый sendmsg.
0 31
Указатель на структуру данных сокета |
Длина структуры данных сокета |
Указатель на список векторов ввода-вывода |
Длина списка векторов ввода-вывода |
Указатель на список прав доступа |
Длина списка прав доступа |
|
В следующем разделе Вы познакомитесь с функцией recvmsg, которая принимает данные в структуру сообщения точно такого же типа, какой использует функция sendmsg. Также вы познакомитесь с другими функциями, предназначенными для приема данных.
20. Прием данных через сокет
В интерфейсе сокетов есть пять функций, предназначенных для приема информации. Они называются: read, readv, recv, recvfrom, recvmsg и соответствуют функциям, использующимся для передачи данных. Например, функции recv и send обладают одинаковым набором параметров. Функция recv принимает данные, a send — посылает. Точно так же одинаков набор параметров и у функций writev и readv. Функция writev передает данные, а readv — принимает.
И та и другая позволяют задать массив адресов памяти, где располагаются данные. Функции recvfrom и recvmsg соответствуют функциям sendto и sendmsg. В табл. 3 приведен список всех соответствующих функций:
Таблица 3. Соответствующие друг другу функции передачи и приема данных интерфейса сокетов
Функция передачи данных |
Соответствующая функция приема данных |
send |
recv |
write |
read |
writev |
readv |
sendto |
recvfrom |
sendmsg |
recvmsg |
Несмотря на то, что интерфейс сокетов имеет соответствующие друг другу функции приема и передачи данных, никто не обязывает соблюдать это соответствие. Предположим, удаленный сетевой компьютер желает передать данные вашему приложению. Чтобы передать программе пришедшие из сокета данные, вовсе не обязательно вызывать строго соответствующую функцию. Так или иначе, данные в сокете все равно представлены единым потоком байтов. Поэтому считать их можно любой из имеющихся функций: recv, read или readv.
Интерфейс сокетов позволяет использовать наиболее удобную в данный момент функцию. Например, если программа не хочет запрашивать у системы большие непрерывные блоки памяти, она может пользоваться функцией readv. В любом случае необходимо учитывать тот факт, что исходный текст программы легче всего читается, если в нем для решения одинаковых задач всегда вызываются однотипные функции.
На рис. 4 изображены системные вызовы интерфейса сокетов, как правило, используемые программами, ориентированными на соединение. Левая часть диаграммы показывает вызовы на стороне сервера, а правая — на стороне клиента. Линии и стрелки между модулями сервера и клиента изображают поток сетевых сообщений между двумя программами.
Рис. 4
Программа-сервер создает сокет, вызывая функцию socket. Строго говоря, программа сервер запрашивает у интерфейса сокетов отвести структуру данных сокета и возвратить дескриптор сокета, который будет использован для дальнейших вызовов сетевых функций. Далее, сервер привязывает сокет к локальному номеру порта протокола.
В следующем разделе будут описаны системные вызовы listen и accept. На данный момент достаточно знать, что вызов listen требует, чтобы сокет прослушивал порт на предмет входящих соединений и подтверждений о доставке. Другими словами, вызов listen переводит сокет в режим пассивного ожидания соединения. Ожидающий соединения сокет высылает каждому передатчику сообщение-подтверждение о том, что сетевой компьютер принял запрос на установление соединения. Однако на самом деле это не означает, что пассивный сокет принял запрос. Чтобы действительно принять запрос и установить соединение, программа должна вызвать функцию accept.
Как показано на рис. 4, программа-клиент также создает сокет, вызывая функцию socket. Однако в отличие от сервера, ориентированной на соединение, например TCP, программе-клиенту нет дела до номера порта протокола, который она получит. То есть ей незачем вызывать функцию bind. Вместо этого программа-клиент, ориентированная на соединение, устанавливает соединение, вызывая функцию connect. После установления соединения передача данных происходит при помощи функций write и read. Кроме них клиент и сервер могут использовать send и recv, а также любую другую функцию, предназначенную для работы с ориентированным на соединение протоколом. Рис. 5 иллюстрирует системные вызовы функций, предназначенных для не ориентированных на соединение протоколов.
Рис. 5
Не ориентированный на соединение сервер на рис. .5 вызывает функции socket и bind так же, как и сервер, ориентированный на соединение. Но поскольку образованный сокет не соединен, для чтения данных программа-клиент использует функцию recvfrom вместо обычных recv или read. Обратите внимание на то, что программа-клиент, изображенная на рис. 5, вызывает функцию bind, но не вызывает connect. Вы помните, что не ориентированные на соединение протоколы не устанавливают никакого предварительного соединения между конечными точками сети. Вместо этого для передачи данных используется функция sendto, требующая от программы указать адрес назначения сообщения в качестве одного из аргументов.
Функция recvfrom также не ожидает соединения. Вместо этого она обрабатывает любые данные, появившиеся на соответствующем (связанном с ней) порту протокола. Получив дейтаграмму из сокета, функция recvfrom записывает как ее содержимое, так и сетевой адрес, с которого она получена. Программы, серверы и клиенты используют сетевой адрес для идентификации процесса передатчика или приемника дейтаграммы. Как и положено, сервер посылает ответную дейтаграмму по адресу, ранее извлеченному функцией recvfrom из пришедшей дейтаграммы.