2015-04-15 34 views
0

我写了一个简单的TFTP服务器,它只处理读请求(RRQ)并且工作正常。如果在5秒内没有收到ACK,服务器应该重新发送当前数据包。在放弃之前,服务器还应该重新发送数据包三次。我试图在传输会话的中间暂停客户端,以查看服务器是否会重新传输数据包,但事实并非如此。问题似乎是服务器不能在while循环中继续。我试图测试它是否逃脱循环,但它没有。我真的不知道为什么它不会重复循环。TFTP中的超时和重传问题

这里是我到目前为止已经写的代码...

#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

感谢很有可能由此引起:

struct timeval tv; 
tv.tv_sec = 5; 

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

+0

谢谢先生,非常感谢。 – user3490561

+0

先生,你有什么想法,为什么要在第二次迭代中打印2次重传数据包然后停止?我的意思是它只等了10秒而不是15 ... – user3490561

+0

先生,请检查我的意见 – user3490561

1

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

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

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

+1

TFTP使用UDP而不是TCP,所以没有连接关闭和终止服务器。你的答案对这种情况无效。杀死客户端在服务器端不会做任何事情。接收ICMP不可达端口可能不足以终止服务器。 –