2

我在两个几乎相同的程序中遇到了这个有趣的小问题。我想做的是在多播套接字上发送一些数据并接收它。现在,如果发件人收到邮件我没问题(我将选项设置为稍后不接收)。

我有两个实现案例。在第一种方法中,我使用初始化sockaddr结构然后绑定到并加入同一个套接字上的多播组的传统方法。然而,这依赖于 IPv4/IPv6,为了避免这种情况,我尝试addrinfo在程序的第二个变体中使用结构。这两个程序都在下面给出。

问题是,在第一个用例中接收到消息,我使用的是常规sockaddr,而在第二种情况下没有接收到消息/套接字描述符。有人可以帮我解释一下为什么会这样吗?

变体 1(带有sockaddr

#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>       /* for nonblocking */
#include <netinet/tcp.h>

fd_set hm_tprt_conn_set;

main()
{
    struct ip_mreq mreq;
    struct sockaddr_in mc_addr;
    int sock_fd ;
    int val;
    int reuse = 1;

    struct sockaddr_in ip;
    struct sockaddr_in src_addr;

    int total_bytes_rcvd=0;
    unsigned int length;
    unsigned char buf[50]; 
    int op_complete = 0;
    int os_error;

    struct timeval select_timeout;
    fd_set read_set;
    int32_t nready; //Number of ready descriptors

    time_t time_val;

    length = sizeof (src_addr);

    sock_fd = socket(AF_INET, SOCK_DGRAM,0);
    if(sock_fd == -1)
    {
        printf("\n Error Opening UDP MCAST socket");
        perror("\n Cause is ");
        exit(0);
    }

    printf("\n Setting the socket to non-blocking mode");
    val = fcntl(sock_fd, F_GETFL , 0);
    val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);

    if (val == -1)
    {
            printf("\n Error while setting socket to non-blocking mode");
            perror("Cause is ");
            sock_fd = -1;
            exit(0);
    } //end if val == -1

    if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        fprintf(stderr, "setsockopt: %d\n", errno);
        perror("Cause is ");
            exit(0);
    }

    FD_SET(sock_fd, &hm_tprt_conn_set);

    printf("\n Construct a mcast address structure");
    /* construct a multicast address structure */

    memset(&mc_addr, 0, sizeof(mc_addr));
    mc_addr.sin_family = AF_INET;
    mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    mc_addr.sin_port = htons(4936);

    memset(&ip, 0, sizeof(ip));
    ip.sin_family = AF_INET;
    ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
    ip.sin_port = htons(4936);

    printf("\n Bind the multicast address structure and port to the recieving socket ");
    if (bind( sock_fd, (struct sockaddr*) &mc_addr, sizeof(mc_addr)) == -1)
    {
        fprintf(stderr, "bind: %d\n", errno);
            perror("\n Cause is ");
        exit(0);
     }

    mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

        if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
    {
        fprintf(stderr, "setsockopt: %d\n", errno);
        perror("\n Cause is ");
            exit(0);
    }

    printf("\nCreated Recv Socket: %d", sock_fd);   


    fflush(stdout);
    memset(&src_addr, 0, sizeof(mc_addr));
    while(1){
    /* Send a multicast */  
    time_val = time(NULL);
    snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
        total_bytes_rcvd = sendto(sock_fd,
                        buf,
                        sizeof(buf),
                           0,
                          (struct sockaddr *)&ip,
                         length );
    printf("\n%d bytes sent.", total_bytes_rcvd);

    /* perform select */                         
        select_timeout.tv_sec = 0;
        select_timeout.tv_usec = 5000000;

        read_set = hm_tprt_conn_set;

        nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
        if(nready == 0)
        {
            /***************************************************************************/
            /* No descriptors are ready                                                */
            /***************************************************************************/
            continue;
        }
        else if(nready == -1)
        {
            perror("Error Occurred on select() call.");
            continue;
        }

    if(FD_ISSET(sock_fd, &read_set))
        {
        printf("\n Recv the data"); 
        total_bytes_rcvd = recvfrom(sock_fd,
                                         buf,
                                      sizeof(buf),
                                       0,
                                  (struct sockaddr *)&src_addr,
                                     &length );

        printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
        printf("\n total byte recieved %d", total_bytes_rcvd);

            /***************************************************************************/
            /* If select returned 1, and it was a listen socket, it makes sense to poll*/
            /* again by breaking out and use select again.                             */
            /***************************************************************************/
            if(--nready <=0)
            {
                printf("\nNo more incoming requests.");
                continue;
            }
        }//end select on listenfd
    }
}

变体 2(带有addrinfo

#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>       /* for nonblocking */
#include <netinet/tcp.h>
#include <netdb.h>      /* AI_PASSIVE and other Macros for getaddrinfo() */

fd_set hm_tprt_conn_set;

main()
{

  struct addrinfo hints, *res, *ressave;
  char target[128] = "127.0.0.1";
  char service[128] = "4936";

    struct ip_mreq mreq;

    int sock_fd ;
    int val;
    int reuse = 1;

    struct sockaddr_in ip;
    struct sockaddr_in src_addr;

    int total_bytes_rcvd=0;
    unsigned int length;
    unsigned char buf[50]; 
    int op_complete = 0;
    int os_error;

    struct timeval select_timeout;
    fd_set read_set;
    int32_t nready; //Number of ready descriptors

    time_t time_val;

    length = sizeof (src_addr);

  sock_fd = socket(AF_INET, SOCK_DGRAM,0);
  if(sock_fd == -1)
  {
      printf("\n Error Opening UDP MCAST socket");
      perror("\n Cause is ");
      exit(0);
  }

  printf("\n Setting the socket to non-blocking mode");
  val = fcntl(sock_fd, F_GETFL , 0);
  val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);

  if (val == -1)
  {
          printf("\n Error while setting socket to non-blocking mode");
          perror("Cause is ");
          sock_fd = -1;
          exit(0);
  } //end if val == -1

  if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  {
      fprintf(stderr, "setsockopt: %d\n", errno);
        perror("Cause is ");
            exit(0);
    }

  FD_SET(sock_fd, &hm_tprt_conn_set);

    printf("\n Construct a mcast address structure");
    /* construct a multicast address structure */

  hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

  if((os_error = getaddrinfo(target, service, &hints, &res)) !=0)
  {
      printf("\n%s",gai_strerror(os_error));
      exit(0);
  }

  ressave = res;

  if(bind(sock_fd, res->ai_addr, res->ai_addrlen) != 0)
    {
        perror("Error binding to port");
        close(sock_fd);
        sock_fd = -1;
    }

    mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
    {
        fprintf(stderr, "setsockopt: %d\n", errno);
        perror("Cause is ");
        exit(0);
    }

  /* Set Destination address */
    memset(&ip, 0, sizeof(ip));
    ip.sin_family = AF_INET;
    ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
    ip.sin_port = htons(4936);

    /* Set to zero address where addresses of sender will be received */
    memset(&src_addr, 0, sizeof(src_addr));

    while(1){

    /* Send a multicast */  
    time_val = time(NULL);
    snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
        total_bytes_rcvd = sendto(sock_fd,
                        buf,
                        sizeof(buf),
                          0,
                          (struct sockaddr *)&ip,
                        length );

    printf("\n%d bytes sent.", total_bytes_rcvd);

    /* perform select */                         
        select_timeout.tv_sec = 0;
        select_timeout.tv_usec = 5000000;

        read_set = hm_tprt_conn_set;

        nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
        if(nready == 0)
        {
            /***************************************************************************/
            /* No descriptors are ready                                                */
            /***************************************************************************/
            continue;
        }
        else if(nready == -1)
        {
            perror("Error Occurred on select() call.");
            continue;
        }

    if(FD_ISSET(sock_fd, &read_set))
        {
        printf("\n Recv the data"); 
        total_bytes_rcvd = recvfrom(sock_fd,
                                  buf,
                                  sizeof(buf),
                                    0,
                                  (struct sockaddr *)&src_addr,
                                  &length );

        printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
        printf("\n total byte recieved %d", total_bytes_rcvd);

            /***************************************************************************/
            /* If select returned 1, and it was a listen socket, it makes sense to poll*/
            /* again by breaking out and use select again.                             */
            /***************************************************************************/
            if(--nready <=0)
            {
                printf("\nNo more incoming requests.");
                continue;
            }
        }//end select on listenfd
    }
}
4

2 回答 2

1

不同之处在于,在第一个变体中,您绑定到 INADDR_ANY,而在第二个变体中,您绑定到 127.0.0.1。未能绑定到 INADDR_ANY 意味着您将不会收到任何多播数据。

您可以使用以下方法解决此问题:

hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;

if((os_error = getaddrinfo(NULL, service, &hints, &res)) !=0)
{
   printf("\n%s",gai_strerror(os_error));
   exit(0);
}

从关于 AI_PASSIVE 的 getaddrinfo 手册页:

如果 node 为 NULL,则每个套接字结构中的网络地址根据在 hints.ai_flags 中设置的 AI_PASSIVE 标志进行初始化。如果设置了 AI_PASSIVE 标志,每个套接字结构中的网络地址将保持未指定。这由服务器应用程序使用,它们打算接受任何网络地址上的客户端连接。如果未设置 AI_PASSIVE 标志,则网络地址将设置为环回接口地址。这由客户端应用程序使用,它们打算连接到在同一网络主机上运行的服务器。

在这种情况下,您将发送到同一台主机,默认情况下,多播数据不会在 localhost 接口上发送出去。您需要使用设置传出多播接口setsockopt的选项进行调用。IP_MULTICAST_IF

通过此更改,我能够使用第二个变体进行发送和接收。

于 2015-06-25T13:08:11.367 回答
0

在你可以之前,bind()你需要有一个工作插座。您将需要循环浏览所有结果。这是您的代码中缺少的内容。

ressave = res;

sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
while(ressave != NULL && (sock < 0 || connect(sock, ressave->ai_addr, ressave->ai_addrlen) < 0)) {
    close(sock);
    if((ressave = ressave->ai_next) != NULL)
        sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
}

在这一点上,你要么找到了一个工作套接字sock,要么没有。当ressave不为 NULL 时,socket 的值sock是有效的。

于 2015-06-25T08:16:14.483 回答