如何从客户端的服务器接收消息? (UDP)

时间:2015-03-22 22:58:56

标签: c++ udp client-server

我试图从我的客户端服务器接收消息,虽然我没有收到任何编译错误,但我的缓冲区不会接受服务器发送的内容。我尝试更改客户端recvfrom中的参数以与客户端sendto中使用的参数相关联,但同样的情况发生,我的memset缓冲区仍为空。我还试过发送一个大小为2的简单的空终止char数组来测试它,并且会出现相同的结果。

服务器:

    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;
    struct sockaddr_storage their_addr;
    char buf[MAXBUFLEN];
    socklen_t addr_len;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) 
            perror("listener: socket");
            continue;
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("listener: bind");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "listener: failed to bind socket\n");
        return 2;
    }

    freeaddrinfo(servinfo);

    while(1){

    addr_len = sizeof their_addr;
    if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
        (struct sockaddr *)&their_addr, &addr_len)) == -1) {
        perror("recvfrom");
        exit(1);
    }

    buf[numbytes] = '\0';

    string toRespond = theMove(buf, AG);  
    char * sendBack = new char[toRespond.size() + 1];
    std::copy(toRespond.begin(), toRespond.end(), sendBack);
    sendBack[toRespond.size()] = '\0';

    sendto(sockfd, testing, strlen(testing), 0, (struct sockaddr *)&their_addr, addr_len);

}

客户端:

    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage src_addr;
    socklen_t src_addr_len = sizeof(src_addr);
    int rv;
    int numbytes;

    if (argc != 3) {
        fprintf(stderr,"usage: talker hostname message\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;

    if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and make a socket
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("talker: socket");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "talker: failed to bind socket\n");
        return 2;
    }

    char * chatBuff = (char*)malloc(sizeof(char)*512);

   while(1){

   scanf("%s", chatBuff);
   if ((numbytes = sendto(sockfd, chatBuff, strlen(chatBuff), 0,
             p->ai_addr, p->ai_addrlen)) == -1) {
        perror("talker: sendto");
        exit(1);
    }
    memset(chatBuff, '\0', sizeof(chatBuff));

   if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)

    {
        puts("throw computer out the stacks");
    }

    puts(chatBuff);

    freeaddrinfo(servinfo);

    printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
    memset(chatBuff, '\0', sizeof(chatBuff));

   }

2 个答案:

答案 0 :(得分:1)

memset(chatBuff, '\0', sizeof(chatBuff));

虽然实际上并不是错误的,但是当你打算在下一行加载它时,这个初始化整个缓冲区是一种无用的废话,并且调用了重新加载的字节数 - 这个返回将允许你确保一个空 - 通过仅设置一个字节来终止字符串。您必须记住的唯一事情是您必须为空值留出足够的空间,方法是加大缓冲区的大小或减少所请求的读取长度。

if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)

在上面的(不必要的和浪费的)行中,您使用' sizeof(chatBuff)'作为缓冲区大小,然后在这里,莫名其妙地,你推入了#strlen(chatBuff)' - 一个RUNTIME CALL,它返回以null结尾的char数组的大小。由于您只是将该数组设置为全为null,因此它返回零,因此您的recvfrom()将始终返回一个太小的'缓冲区'除非您收到空数据报,否则会出错。

所以:

int bytesRec=recvfrom(sockfd, chatBuff, sizeof(chatBuff)-1, 0, (struct sockaddr*)&src_addr, &src_addr_len);
if(bytesRec<1) puts("throw computer out the stacks")
else chatBuff[bytesRec]=0;

答案 1 :(得分:0)

在您的服务器代码中,您将一些名为testing的未知缓冲区作为要发送的缓冲区传递,但您应该通过sendBack缓冲区:

sendto(sockfd, sendBack, strlen(sendBack), 0, (struct sockaddr *)&their_addr, addr_len);

就此而言,您可以取消sendBack并直接从toRespond发送数据:

sendto(sockfd, toRespond.c_str(), toResponse.size(), 0, (struct sockaddr *)&their_addr, addr_len);

在您的服务器和客户端代码中,您在调用AF_UNSPEC时使用getaddrinfo()。这在服务器中是可以的,但在客户端通常不行。想象一下,如果服务器绑定到IPv6地址会发生什么,但客户端会创建IPv4套接字,反之亦然。显然不匹配,无法进行通信(除非服务器创建可以同时接受IPv4和IPv6数据包的双栈IPv6套接字)。因此,在您的客户端代码中,您不应使用AF_UNSPEC。使用AF_INETAF_INET6,具体取决于服务器实际绑定的内容。如果您必须在客户端使用AF_UNSPEC,则需要为每个可能的服务器IP地址调用sendto()recvfrom(),直到您收到其中一个的响应为止。

最后,在您的客户端代码中,您对recvfrom()的调用假设响应数据的大小不会与sendto()发送的数据大小相同。实际情况是这样的吗?您没有显示theMove()对服务器接收的数据的影响,或者它是如何生成响应的。至少,您应该在调用strlen()时将recvfrom()替换为512。您的客户端代码也假设服务器的响应将以空值终止,但服务器不会在它回显的数据中发送空终止符。因此,您需要终止传递给puts()的缓冲区。