Проблема с таймаутом и повторной передачей в TFTP

Я написал простой TFTP-сервер, который обрабатывает только запросы на чтение (RRQ), и он работает нормально. Предполагается, что сервер повторно передаст текущий пакет данных, если ACK не получен в течение 5 секунд. Сервер также должен повторно передать пакет три раза, прежде чем сдаться. Я попытался приостановить работу клиента в середине сеанса передачи, чтобы посмотреть, будет ли сервер повторно передавать пакет данных, а это не так. Кажется, проблема в том, что сервер не продолжает while петля. Я пытался проверить, не выходит ли он из цикла, но этого не произошло. Я действительно не могу понять, почему он не повторяет цикл снова.

Вот код, который я написал до сих пор...

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define TIMEOUT 5000
#define RETRIES 3

void sendFile (char *Filename, char *mode, struct sockaddr_in client, int tid)
{
    struct timeval tv;
    tv.tv_sec = 5;
    char path[70] = "tmp/";
    char filebuf [1024];
    int count = 0, i;  // Number of data portions sent 
    unsigned char packetbuf[1024];
    char recvbuf[1024];
    socklen_t recv_size;

    int sock = socket(PF_INET, SOCK_DGRAM, 0);
    socklen_t optionslength = sizeof(tv);
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);

    FILE *fp;
    char fullpath[200];
    strcpy(fullpath, path);
    strncat(fullpath, Filename, sizeof(fullpath) -1);
    fp = fopen(fullpath, "r");
    if (fp == NULL)
        perror("");

    memset(filebuf, 0, sizeof(filebuf));
    while (1)
    {   
        int acked = 0;
        int ssize = fread(filebuf, 1 , 512, fp);
        count++;        
        sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
        memcpy((char *) packetbuf + 4, filebuf, ssize);
        packetbuf[2] = (count & 0xFF00) >> 8;
        packetbuf[3] = (count & 0x00FF);

        int len = 4 + ssize;        

        memset(recvbuf, 0, 1024);
        printf("\nSending Packet #%d", count);
        sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));

        for (i=0; i<3; i++)
        {
            int result = recvfrom(sock, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);

            if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
            {
                printf("\nRetransmitting Packet #%d",count);
                sendto(sock, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
            }

            else if (result == -1)
            {

            }

            else 
            {
                if (tid == ntohs(client.sin_port))
                {
                    printf("\nReceived Ack #%d",count);
                    acked++;
                    break;
                }

                else
                    continue;
            }
        }

        if (acked!=1)
        {
            puts("\nGave Up Transmission");
            break;
        }

        if (ssize != 512)
        {
            break;

        }
    }
}


int main()
{
    int udpSocket, nBytes, tid, pid, status;
    char buffer[1024], filename[200], mode[20], *bufindex, opcode;
    struct sockaddr_in serverAddr, client;
    struct sockaddr_storage serverStorage;
    socklen_t addr_size;

    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(69);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero); 

    bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
    pid = fork();

    while(1)
    {
        int client_len = sizeof(client);
        memset (buffer, 0, 1024);
        nBytes = 0;
        while (errno == EAGAIN || nBytes == 0)
        {
            waitpid(-1, &status, WNOHANG);
            nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &client_len);

        }

        bufindex = buffer;
        bufindex++;

        // Record the client port...
        tid = ntohs(client.sin_port); 

        // Extracting the opcode from the packet...     
        opcode = *bufindex++;

        // Extracting the filename from the packet...
        strncpy(filename, bufindex, sizeof(filename)-1);

        bufindex += strlen(filename) + 1;

        // Extracting the mode from the packet...       
        strncpy(mode, bufindex, sizeof(mode)-1);

        // If we received an RRQ...
        if (opcode == 1)
        {
            puts("Received RRQ Packet");
            pid = fork();
            if (pid == 0)
            {
                sendFile(filename, mode, client, tid);
                exit(1);
            }
        }
    }   

    return 0;
}

Примечание. Для тестирования сервера вы можете использовать стандартный клиент TFTP, поставляемый с linux.

Заранее спасибо:)

2 ответа

Решение

Вероятно, вызвано этим:

struct timeval tv;
tv.tv_sec = 5;

ТВ выделяется в стеке. Память стека неинициализирована и поэтому содержит случайные значения. Так что вам нужно явно установить tv.tv_usec в 0.

Метод тестирования OP;'- убить клиента. Однако это убивает сокет. Не очень хороший метод тестирования.

man-страница linux для getaddrinfo содержит пример кода сервера и кода клиента для службы эха. Основная логика может быть применена к сервису tftp.

Помните, что у tftp есть несколько состояний. Эти состояния должны быть отражены в логике кода.

Другие вопросы по тегам