время эксплуатации.

Клиент 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() для установки удаленного конца сокета на том адресе, от которого поступил запрос на файл, и начинается передача файла. После отправки каждого блока сервер ожидает пакет подтверждения, прежде чем продолжить передачу. Когда приходит последний пакет подтверждения, сервер закрывает сокет и возвращается к главному циклу.

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

  1: /* tftpserver.c */

  2:

  3: /* Это частичная реализация tftp. Она не поддерживает даже тайм-ауты

  4:    или повторную передачу пакетов, и она не очень хорошо

  5:    обрабатывает непредвиденные события.*/

  6:

  7: #include <netdb.h>

  8: #include <stdio.h>

  9: #include <stdlib.h>

 10: #include <string.h>

 11: #include <sys/socket.h>

 12: #include <unistd.h>

 13: #include <fcntl.h>

 14:

 15: #include 'sockutil.h' /* некоторые служебные функции */

 16:

 17: #define RRQ   1 /* запрос на чтение */

 18: #define DATA  3 /* блок данных */

 19: #define ACK   4 /* подтверждение */

 20: #define ERROR 5 /* возникла ошибка */

 21:

 22: /* коды ошибок tftp */

 23: #define FILE_NOT_FOUND 1

 24:

 25: struct tftpPacket {

 26:  short opcode;

 27:

 28:  union {

 29:   char bytes[514]; /* самый большой блок, который мы

 30:                       можем обработать, содержит 2 байта

 31:                       для номера блока и 512 для данных */

 32:   struct {

 33:    short code;

 34:    char message[200];

 35:   } error;

 36:

 37:   struct {

 38:    short block;

 39:    char bytes[512];

 40:   } data;

 41:

 42:   struct {

 43:    short block;

 44:   } ack;

 45:  } u;

 46: };

 47:

 48: void sendError(int s, int errorCode) {

 49:  struct tftpPacket err;

 50:  int size;

 51:

 52:  err.opcode = htons(ERROR);

 53:

 54:  err.u.error.code = htons(errorCode); /* файл не найден */

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату