#include
#include
int send(int s, const void * data, size_t len, int flags);
int sendto(int s, const void * data, size_t len, int flags,
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] Полное описание 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 работает без установки соединений.
Читать дальше