2014-03-24 109 views
0

正在處理旨在模擬網絡中數據層的程序。我有正確的消息到服務器,但是,客戶端沒有收到來自服務器的ACK幀。這導致我的程序無休止地等待。任何幫助解決這個問題的讚賞。UDP服務器未對客戶端作出響應

發件人

#include <stdio.h> 
#include <unistd.h> 
#define MAXFRAME 97 
main(int argc, char* argv[]){ 
    char *frame; 
    int len = 0; 
    int c; 
    dlinits("spirit.cba.csuohio.edu", 43525); 
    frame = malloc(MAXFRAME); 

    FILE *file = fopen(argv[1], "r"); 
    if (file == NULL) 
     return NULL; 

    while ((c = fgetc(file)) != EOF) 
    { 
     if(len == (MAXFRAME-1)){ 
      dlsend(frame, len, 0); 
      len = 0; 
      memset(frame,0,strlen(frame)); 
     }  

     frame[len++] = (char) c; 
    } 

    dlsend(frame, len, 1); 




} 

接收機

#include <string.h> 
#include <unistd.h> 
char* dlrecv(); 

main(){ 
    char* test[100]; 
    dlinitr(43525); 
    while(1){ 
     strcpy(test,dlrecv()); 

     printf("%s\n", test); 
    } 


} 

數據層

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#define BUFMAX 100 

static int sk; 
static struct sockaddr_in remote; 
static struct sockaddr_in local; 
static int fnum = 0; 
static expFra = 0x00; 

dlinits(char* host, int port){//initialize sender 

    struct hostent *hp; 
    sk = socket(AF_INET, SOCK_DGRAM, 0); 

    remote.sin_family = AF_INET; 

    hp = gethostbyname(host); 
    if (hp == NULL){ 
     printf("Can't find host name\n"); 
     exit(1); 
    } 

    bcopy(hp->h_addr,&remote.sin_addr.s_addr,hp->h_length); 

    remote.sin_port = ntohs(port); 
} 

dlinitr(int port){//initialize receiver 
    int rlen = sizeof(remote); 
    int len = sizeof(local); 
    char buf[BUFMAX]; 

    sk = socket(AF_INET,SOCK_DGRAM,0); 

    local.sin_family = AF_INET; 
    local.sin_addr.s_addr = INADDR_ANY; 
    local.sin_port = htons(port); 
    bind (sk, &local,sizeof(local)); 

    getsockname(sk,&local,&len); 

} 

dlsend(char* msg, int len, int end){//send data 
    int header = 0x00; 
    int result; 
    char *ackframe = malloc(3); 
    unsigned char *nmsg; 
    nmsg = malloc(100); 
    if ((fnum%2) == 1){ 
     header = header|0x02; 
    } 
    if (end == 1){ 
     header = header|0x40; 
    } 
    header = header^0xff; 
    printf("%x\n %x\n", header, 0); 
    nmsg[0] = (char)header; 
    len++; 
    printf("%s\n", nmsg); 
    memcpy(nmsg + 1, msg, strlen(msg)); 
    result = crc(nmsg, len); 
    nmsg[len++] = ((result >> 8) & 0xff); 
    nmsg[len++] = (result & 0xff); 


    printf("%s\n", nmsg); 
    sendto(sk,nmsg,len,0,&remote,sizeof(remote)); 

    read(sk,ackframe,3); 
    printf("Ack Received: %s\n", ackframe); 

    fnum++; 
} 

char* dlrecv(){//receive data 
    int result; 
    int header; 
    int ACK = 1; 
    char alen = 1; 
    char *ackframe = malloc(3); 
    unsigned char* msg = malloc(100); 
    while (ACK){ 
     recvfrom(sk,msg,BUFMAX,0,&remote,sizeof(remote)); 
     int len = strlen(msg); 
     result = crc(msg, len); 
     if (result == 0){ 
      msg[--len] = 0; 
      msg[--len] = 0; 
      header = msg[0]; 
      printf("Header %x expFra %x\n", header, expFra); 
      header = header^0xff; 
      printf("Header %x expFra %x\n", header, expFra); 
      if ((header<<4) == (expFra<<4)){ 
       expFra = expFra^0x02; 
       ackframe[0] = (0x10|header); 
       result = crc(ackframe, alen); 
       ackframe[alen++] = ((result >> 8) & 0xff); 
       ackframe[alen++] = (result & 0xff); 
       sendto(sk,ackframe,strlen(ackframe),0,&remote,sizeof(remote)); 
       printf("Ack Sent: %s\n", ackframe); 
       ACK = 0; 

      } 
     } 
    } 
    printf("%s\n", msg); 
    return ++msg; 
} 

編輯這些都在同一臺機器上工作的時刻。 編輯我跑了一個檢查使用errno,它返回錯誤22爲dlrecv裏面的sendto。

+0

你能說一下你如何運行這些程序嗎?它們是在同一臺機器上運行(本地主機)還是涉及多臺機器? UDP傳遞並不能保證,大多數防火牆會丟棄這樣的流量等等,所以你的網絡拓撲結構中的一些信息是有保證的。 – Soren

+2

每當我在網絡代碼中看到strlen()時,我都非常緊張... –

回答

2

我對於UDP的使用經驗是,read()(你在dlsend()的末尾使用的)非常容易被碰到,尤其是在與sendto()配對時。除非有充分的理由不這樣做,否則更改read()recvfrom()應該可以解決問題。

您的代碼還會拋出一個批號爲的不匹配類型的警告。他們有點不傷害,但跟蹤其他任何事情都變得更加複雜。

之後,最終確認sendto()正在使用錯誤的套接字數據。扯遠了,原因是你在(sizeof(remote))中傳遞一個整數作爲前一個recvfrom()調用中地址大小的指針。如果給定的初始尺寸太小,recvfrom()會產生不可靠的結果。如果它需要的空間比它少,它會改變這個值來告訴你它使用了什麼。

所以,你需要聲明初始化爲sockaddr_in結構大小的整數指針傳遞給它作爲最後一個參數。通過這些更改,假設服務器到達sendto()函數(您的示例只有一個條件分支),您將獲得正確的地址值並能夠發送確認。

重要的經驗教訓應該是(a)確保所有類型都正確並檢查每個警告,並(b)檢查每個套接字調用的返回值,並在出現-1時打印錯誤。

+0

我提出了建議的更改,但結果是一樣的。我仍然在第一封郵件之後掛,因爲寄件人沒有收到任何郵件。 – Ranma344

+0

啊......我沒有把它追蹤下來,但是'遙控器'正在被搗毀。你會得到一個'無效的參數'錯誤,如果你檢查'sin_family'和'sin_port',它們將被清零。 –

+0

我明白了。我在看,但我似乎無法找到發生這種情況的原因。 – Ranma344