2014-03-24 31 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