После создания сокета сервер ожидает получение дейтаграммы путем вызова recvfrom()
. Функция handleRequest()
, которая активизируется в строке 181, преобразует запрашиваемый файл и возвращает его. После этого вызова сервер еще раз активизирует recvfrom()
и ожидает запрос от следующего клиента.
Комментарий, расположенный перед вызовом handleRequest()
, сообщает, что данный сервер можно легко переключить с итеративного сервера на параллельный, позволив каждому вызову handleRequest()
работать как самостоятельному процессу.
В то время как главная часть программы использует неподключенный UDP-сокет (позволяющий любому клиенту соединение с ним), handleSocket()
применяет для преобразования файла подключенный UDP-сокет [144] Спецификация tftp-протокола требует, чтобы серверы посылали данные клиенту на номер порта, отличающийся от порта, на котором сервер ожидает новые соединения. При этом нетрудно создать параллельный сервер, поскольку каждый сокет сервера предназначен только для одного клиента.
. После анализа имени файла, который нужно передать, и проверки правильности режима преобразования в строке 93 создается сокет с тем же самым семейством, типом и протоколом, с которыми контактировал сервер. Затем используется connect()
для установки удаленного конца сокета на том адресе, от которого поступил запрос на файл, и начинается передача файла. После отправки каждого блока сервер ожидает пакет подтверждения, прежде чем продолжить передачу. Когда приходит последний пакет подтверждения, сервер закрывает сокет и возвращается к главному циклу.
Данный сервер, как правило, запускается с единственным аргументом — номером порта. Для проверки можно применить стандартную клиентскую программу tftp
, где первый аргумент является именем хоста для соединения (неплохим выбором будет localhost
), а второй — номером порта, на котором работает сервер. После запуска клиента нужно активизировать команду bin
, при этом файлы будут запрашиваться в режиме octet
, а не в стандартном режиме netascii
. Как только это сделано, команда get
позволит передать любой файл от сервера клиенту.
1: /* tftpserver.c */
2:
3: /* Это частичная реализация tftp. Она не поддерживает даже тайм-ауты
4: или повторную передачу пакетов, и она не очень хорошо
5: обрабатывает непредвиденные события.*/
6:
7: #include
8: #include
9: #include
10: #include
11: #include
12: #include
13: #include
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); /* файл не найден */
55: switch (errorCode) {
56: case FILE_NOT_FOUND:
57: strcpy(err.u.error.message, "файл не найден");
58: break;
59: }
60:
61: /* 2 байта кода операции, 2 байта кода ошибки, сообщение и '\0' */
62: size = 2 + 2 + strlen(err.u.error.message) + 1;
63: if (send(s, &err, size, 0) != size)
64: die("erarorsend");
65: }
66:
67: void handleRequest(struct addrinfo tftpAddr,
68: struct sockaddr remote, int remoteLen,
69: struct tftpPacket request) {
70: char * fileName;
71: char * mode;
72: int fd;
73: int s;
74: int size;
75: int sizeRead;
76: struct tftpPacket data, response;
77: int blockNum = 0;
78:
79: request.opcode = ntohs(request.opcode);
80: if (request.opcode != RRQ) die("неверный код операции");
81:
82: fileName = request.u.bytes;
83: mode = fileName + strlen(fileName) + 1;
84:
85: /* здесь поддерживается только режим bin */
86: if (strcmp(mode, "octet")) {
87: fprintf(stderr, "неверный режим %s\n", mode);
Читать дальше