Чтение онлайн

ЖАНРЫ

Разработка приложений в среде Linux. Второе издание

Троан Эрик В.

Шрифт:

Преимущество использования подключенных UDP-сокетов состоит в том, что только та машина и порт, которые указаны как удаленный адрес для сокета, могут передавать дейтаграммы в данный сокет. Произвольный IP-адрес и порт может посылать дейтаграммы в неподключенный UDP-сокет, который требуется в некоторых случаях (именно через него новые клиенты впервые связываются с серверами), однако при этом программы должны отслеживать место отправки дейтаграмм.

17.6.2. Отправка и получение дейтаграмм

Для отправки и получения UDP-пакетов обычно используются четыре системных вызова [141] :

send
,
sendto
,
recv
,
recvfrom
[142] .

#include <sys/types.h>

#include <sys/sockets.h>

int send(int s, const void * data, size_t len, int flags);

int sendto(int s, const void * data, size_t len, int flags,

141

Данные

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

142

Возможно также применение системных вызовов

sendmsg
и
recvmsg
, однако необходимость в этом встречается редко.

 const struct sockaddr * to, socklen_t toLen);

int recv(int s, void * data, size_t maxlen, int flags);

int recvfrom(int s, void * data, size_t maxlen, int flags,

 struct sockaddr * from, socklen_t * fromLen);

Здесь во всех случаях параметр

flags
всегда равен нулю. В других ситуациях он может принимать множество значений, они подробно рассматриваются в [33].

Первый из названных вызовов

send
может применяться только для тех сокетов, для которых IP-адрес назначения и порт устанавливались через вызов
connect
. Он посылает первые
len
байтов, на которые указывает
data
, на другой конец сокета
s
. Данные передаются как единая дейтаграмма. Если параметр
len
задает слишком большое количество данных для передачи в одной дейтаграмме, то в переменной
errno
возвращается значение
EMSGSIZE
.

Следующий системный вызов

sendto
работает аналогично
send
, но позволяет указывать IP-адрес и номер порта назначения для неподключенных сокетов. Последние два параметра являются указателями на адрес сокета и длину адреса сокета. Применение этой функции не устанавливает адрес назначения для сокета; он остается неподключенным. Последующие вызовы
sendto
могут передавать дейтаграммы в другие пункты назначения. Если аргумент to равен
NULL
, то функция
sendto
ведет себя точно также как и
send
.

Системные вызовы

recv
и
recvfrom
подобны
send
и
sendto
, но они получают дейтаграммы, а не отправляют их. Оба вызова записывают одну дейтаграмму в
data
(не более чем
*maxlen
байт) и отбрасывают некоторую часть дейтаграммы, которая не помещается в буфер. Удаленный адрес, отправивший дейтаграмму, сохраняется в параметре
from
функции
recvmsg
, если только его длина не превышает
fromLen
байт.

17.6.3. Простой tftp-сервер

Данный простой tftp-сервер иллюстрирует отправку и получение UDP-дейтаграмм как для подключенных, так и для неподключенных сокетов. Протокол tftp представляет собой несложный протокол передачи файлов, построенный на основе UDP [143] . Он часто используется встроенными компьютерными программами для пересылки первоначального загрузочного образа при сетевой загрузке. Сервер, который мы предлагаем рассмотреть, обладает рядом ограничений, поэтому он непригоден для какой-либо практической работы.

143

Полное описание tftp можно найти в [33] и [34].

• С сервером одновременно может взаимодействовать только один клиент (этот недостаток легко устранить).

• Сервер может только отправлять файлы, но не может получать.

• Отсутствуют

условия для ввода ограничений на передачу файлов анонимному удаленному пользователю.

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

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

netascii
(выполняет некоторые простые преобразования файла) и
octet
(передает файл точно в таком же состоянии, в каком он находится на диске). Рассматриваемый сервер поддерживает только режим
octet
, поскольку он проще.

При получении запроса для чтения tftp-сервер посылает файл (512 байт за один раз). Каждая дейтаграмма содержит номер блока (нумерация начинается с единицы). Когда клиент получает блок данных, содержащий менее 512 байтов, он знает, что файл получен должным образом. После каждой дейтаграммы клиент посылает ответную дейтаграмму с номером блока, подтверждающую, что данный блок успешно получен. Как только сервер видит подтверждение, он отправляет следующий блок данных.

Основной формат дейтаграммы определен в строках 17-46. Некоторые константы указывают тип посылаемой дейтаграммы, а также код ошибки, отправляемой в том случае, если запрашиваемый файл не существует (все остальные ошибки обрабатываются непосредственно сервером). Структура

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

Первая часть

main
(строки 156—169) создает UDP-сокет и устанавливает номер локального порта с помощью вызова
bind
. Последний является либо официальной tftp-службой, либо портом, указанным в качестве единственного аргумента командной строки программы. В отличие от нашего примера TCP-сервера здесь нет необходимости вызывать ни
listen
, ни
accept
, поскольку UDP работает без установки соединений.

После создания сокета сервер ожидает получение дейтаграммы путем вызова

recvfrom
. Функция
handleRequest
, которая активизируется в строке 181, преобразует запрашиваемый файл и возвращает его. После этого вызова сервер еще раз активизирует
recvfrom
и ожидает запрос от следующего клиента.

Комментарий, расположенный перед вызовом

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

В то время как главная часть программы использует неподключенный UDP-сокет (позволяющий любому клиенту соединение с ним),

handleSocket
применяет для преобразования файла подключенный UDP-сокет [144] . После анализа имени файла, который нужно передать, и проверки правильности режима преобразования в строке 93 создается сокет с тем же самым семейством, типом и протоколом, с которыми контактировал сервер. Затем используется
connect
для установки удаленного конца сокета на том адресе, от которого поступил запрос на файл, и начинается передача файла. После отправки каждого блока сервер ожидает пакет подтверждения, прежде чем продолжить передачу. Когда приходит последний пакет подтверждения, сервер закрывает сокет и возвращается к главному циклу.

144

Спецификация tftp-протокола требует, чтобы серверы посылали данные клиенту на номер порта, отличающийся от порта, на котором сервер ожидает новые соединения. При этом нетрудно создать параллельный сервер, поскольку каждый сокет сервера предназначен только для одного клиента.

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

tftp
, где первый аргумент является именем хоста для соединения (неплохим выбором будет
localhost
), а второй — номером порта, на котором работает сервер. После запуска клиента нужно активизировать команду
bin
, при этом файлы будут запрашиваться в режиме
octet
, а не в стандартном режиме
netascii
. Как только это сделано, команда
get
позволит передать любой файл от сервера клиенту.

Поделиться с друзьями: