【问题标题】:How to receive a message from the server in a client? (UDP)如何在客户端接收来自服务器的消息? (UDP)
【发布时间】:2015-03-22 22:58:56
【问题描述】:

我试图在我的客户端中接收来自服务器的消息,虽然我没有收到任何编译错误,但我的缓冲区不会接收服务器正在发送的内容。我尝试更改客户端中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));

   }

【问题讨论】:

  • 你为什么要在客户的时候释放servinfo?
  • 第一个停靠港,男人 strlen。
  • 调试 101 - 如果您的调用失败,请先将所有值参数加载到临时本地变量中并打印出来。要么,要么使用实际的调试器来检查它们。在此处发布之前执行此操作。

标签: c++ udp client-server


【解决方案1】:
memset(chatBuff, '\0', sizeof(chatBuff));

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

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

在上面的(不必要和浪费的)行中,您使用 'sizeof(chatBuff)' 作为缓冲区大小,但是在这里,莫名其妙地,你推入了 'strlen(chatBuff)' - 一个返回大小的 RUNTIME CALL以 null 结尾的 char 数组。由于您只是将该数组设置为全空,因此它返回零,因此您的 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;

【讨论】:

    【解决方案2】:

    在您的服务器代码中,您传递了一些名为 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);
    

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

    最后,在您的客户端代码中,您对recvfrom() 的调用假定响应数据的大小不会超过使用sendto() 发送的数据的大小。真的是这样吗?您没有展示theMove() 对服务器接收到的数据做了什么,或者它如何生成响应。在调用recvfrom() 时,至少应该将strlen() 替换为512。您的客户端代码还假设服务器的响应将以空值终止,但服务器并未在其回显的数据中发送空值终止符。所以你需要终止你传递给puts()的缓冲区。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2019-10-13
      相关资源
      最近更新 更多