TFTP中的超时和重传问题

时间:2015-04-15 22:18:40

标签: c multithreading sockets fork tftp

我写了一个只处理读取请求(RRQ)的简单TFTP服务器,它运行正常。如果在5秒内没有收到ACK,则服务器应该再次重新发送当前数据包。在放弃之前,服务器还应该重新发送数据包三次。我试图在传输会话中暂停客户端,以查看服务器是否会再次重新传输数据包,但它没有。问题似乎是服务器没有在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;
}

注意:您可以使用linux附带的标准TFTP客户端来测试服务器。

提前致谢:)

2 个答案:

答案 0 :(得分:2)

可能由此引起:

struct timeval tv;
tv.tv_sec = 5;

电视在堆栈上分配。堆栈内存未初始化,因此其中包含随机值。所以你需要明确地将tv.tv_usec设置为0。

答案 1 :(得分:1)

OP的测试方法是杀死客户端。但是,这会杀死套接字。不是一个好的测试方法。

getaddrinfo的linux手册页包含服务器代码和echo服务的客户端代码的示例。基本逻辑可以应用于tftp服务。

请记住,tftp有几种状态。这些状态应该反映在代码逻辑中。