2

私は、2 つのほぼ同一のプログラムでこの面白い小さな問題を抱えています。私がやろうとしているのは、マルチキャストソケットでデータを送信して受信することです。今のところ、送信者がメッセージを受信すれば問題ありません (後で受信しないオプションを設定します)。

私は2つの実装ケースを持っています。最初のアプローチでは、sockaddr構造を初期化してからバインドし、同じソケットのマルチキャスト グループに参加する従来の方法を使用しています。ただし、これは IPv4/IPv6 に依存するため、これを回避するためaddrinfoに、プログラムの 2 番目のバリアントで構造体を使用しようとしました。両方のプログラムを以下に示します。

問題は、メッセージが最初のユースケースでsockaddr受信されていることです.2番目のケースではメッセージが受信されず、ソケット記述子が設定されていません。誰かが私を助けて、なぜこれが起こっているのか説明してもらえますか?

バリアント 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 にバインドしているのに対し、2 番目の亜種では 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 の man ページから:

node が NULL の場合、各ソケット構造体のネットワーク アドレスは、hints.ai_flags に設定されている AI_PASSIVE フラグに従って初期化されます。AI_PASSIVE フラグが設定されている場合、各ソケット構造体のネットワーク アドレスは未指定のままになります。これは、任意のネットワーク アドレスでクライアント接続を受け入れるサーバー アプリケーションによって使用されます。AI_PASSIVE フラグが設定されていない場合、ネットワーク アドレスはループバック インターフェイス アドレスに設定されます。これは、同じネットワーク ホスト上で実行されているサーバーに接続するクライアント アプリケーションによって使用されます。

この場合、同じホストに送信していますが、デフォルトでは、マルチキャスト データは localhost インターフェイスに送信されません。発信マルチキャスト インターフェイスを設定するオプションを指定setsockoptして呼び出す必要があります。IP_MULTICAST_IF

この変更により、2 番目のバリアントで送受信できるようになりました。

于 2015-06-25T13:08:11.367 に答える