2015-04-15 69 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不可達端口可能不足以終止服務器。 –