如何检查客户端是否已加入多播组?

3

最终目标:

让服务器的父进程知道哪些客户端加入或离开了多播组。到目前为止,我只尝试过检查客户端加入,因为我认为检查离开的客户端是类似的。

我的方法:

通过select()检查多播组套接字中的变化。

加入后,客户端执行一个针对(服务器程序的)父进程的sendto()。服务器中的select()应该识别任何变化,但显然没有,因此retval != 0从来不会成立。

到目前为止的结果

我已经尝试使用许多不同的IP地址和常量,如INADDR_ANY,但我只能将消息发送回客户端,并且客户端解释为服务器程序发送的消息。最常见的结果是服务器程序根本没有收到任何消息。

这是我的客户端代码:

/* Receiver/client multicast Datagram*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_MSG 100

struct sockaddr_in localSock, servSock;
struct ip_mreq group;
int sd, n;
int datalen, mcastport;
char msg[MAX_MSG];

int main(int argc, char *argv[])
{

    if(argc!=3) {
        printf("usage : %s <address> <port>\n",argv[0]);
        exit(0);
    }

    mcastport = atoi(argv[2]);


    /* Create a datagram socket on which to receive. */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0)
    {
        perror("Opening datagram socket error");
        exit(1);
    }
    else
        printf("Opening datagram socket....OK.\n");

    /* Enable SO_REUSEADDR to allow multiple instances of this */
    /* application to receive copies of the multicast datagrams. */
    {
        int reuse = 1;
        if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
        {
            perror("Setting SO_REUSEADDR error");
            close(sd);
            exit(1);
        }
        else
            printf("Setting SO_REUSEADDR...OK.\n");
    }

    /* Bind to the proper port number with the IP address */
    /* specified as INADDR_ANY. */
    memset((char *) &localSock, 0, sizeof(localSock));
    localSock.sin_family = AF_INET;
    localSock.sin_port = htons(mcastport);
    localSock.sin_addr.s_addr = INADDR_ANY;
    if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
    {
        perror("Binding datagram socket error");
        close(sd);
        exit(1);
    }
    else
        printf("Binding datagram socket...OK.\n");

    printf("Enter the group's name you want to join:\n");
    scanf("%s", msg);

    /* Join the multicast group 226.1.1.1 on the local IP address */
    /* interface. Note that this IP_ADD_MEMBERSHIP option must be */
    /* called for each local interface over which the multicast */
    /* datagrams are to be received. */
    group.imr_multiaddr.s_addr = inet_addr(argv[1]);
    group.imr_interface.s_addr = inet_addr("127.0.0.1");
    if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
    {
        perror("Adding multicast group error");
        close(sd);
        exit(1);
    }
    else
        printf("Adding multicast group...OK.\n");

    /* Initialize the group sockaddr structure with a */
    /* group address of 226.1.1.1 and port given by user. */
    memset((char *) &servSock, 0, sizeof(servSock));
    servSock.sin_family = AF_INET;
    servSock.sin_addr.s_addr = inet_addr(argv[1]);
    servSock.sin_port = htons(mcastport);

    if(sendto(sd, "", 1, 0, (struct sockaddr*) &servSock, sizeof(servSock)) < 0)
    {perror("Sending datagram message error");}
    else
        printf("Sending datagram message...OK\n");

    /* Read from the socket. */
    if((n=read(sd, msg, MAX_MSG)) < 0)
    {
        perror("Reading datagram message error");
        close(sd);
        exit(1);
    }
    else
    {
        printf("Reading datagram message...OK.\n");
        printf("The message from multicast server is: \"%s\"\n", msg);
    }

    msg[n] = '\0';

    return 0;
}

这是我的服务器程序代码:
/* Send Multicast Datagram code*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

#include <stdio.h>
#include <unistd.h>
#include <string.h> /* for strncpy, memset */

#define MAX_MSG 100

struct in_addr localInterface;
struct sockaddr_in groupSock, cliAddr;
int sd, mcastport, maxJoin, maxJoined = 0, pipefd[2], cliLen, cpid;
char msg[MAX_MSG], groupName[MAX_MSG];

int main (int argc, char *argv[ ])
{


    /* check command line args */
    if(argc < 2) {
        printf("usage : %s <port> \n", argv[0]);
        exit(1);
    }

    mcastport = atoi(argv[1]);

    /* Create a datagram socket on which to send. */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0)
    {
        perror("Opening datagram socket error");
        exit(1);
    }
    else
        printf("Opening the datagram socket...OK\n");

    /* Initialize the group sockaddr structure with a */
    /* group address of 225.1.1.1 and port given by user. */
    memset((char *) &groupSock, 0, sizeof(groupSock));
    groupSock.sin_family = AF_INET;
    groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
    groupSock.sin_port = htons(mcastport);

    printf("Create a group: ");
    scanf("%s", groupName);

    printf("Maximum number of clients that can join the group? ");
    scanf("%d", &maxJoin);

    /* Disable loopback so you do not receive your own datagrams.
    {
    char loopch = 0;
    if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
    {
    perror("Setting IP_MULTICAST_LOOP error");
    close(sd);
    exit(1);
    }
    else
    printf("Disabling the loopback...OK.\n");
    }
     */

    /* Set local interface for outbound multicast datagrams. */
    /* The IP address specified must be associated with a local, */
    /* multicast capable interface. */
    printf("Setting the local interface...");
    localInterface.s_addr = inet_addr("127.0.0.1");
    if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
    {
        perror("error");
        exit(1);
    }
    else
        printf("OK\n");



    if((cpid = fork()) == 0) //child process --sends messages
    {
        /* Send a message to the multicast group specified by the*/
        /* groupSock sockaddr structure. */
        printf("Enter a message to send: \n");
        scanf("%s", msg);

        if(sendto(sd, msg, strlen(msg)+1, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0)
        {perror("Sending datagram message error");}
        else
            printf("Sending datagram message...OK\n");

        /* Try the re-read from the socket if the loopback is not disable
        if(read(sd, databuf, datalen) < 0)
        {
        perror("Reading datagram message error\n");
        close(sd);
        exit(1);
        }
        else
        {
        printf("Reading datagram message from client...OK\n");
        printf("The message is: %s\n", databuf);
        }
         */

        exit(EXIT_SUCCESS);
    }
    else //parent process --checks for JOINs and QUITs
    {
        fd_set rfds;
        struct timeval tv;
        int retval, status;

        while (waitpid(cpid, &status, WNOHANG) != cpid)
        {
            /* Watch stdin (fd 0) to see when it has input. */
            FD_ZERO(&rfds);
            FD_SET(sd, &rfds);

            /* Wait up to five seconds. */
            tv.tv_sec = 5;
            tv.tv_usec = 0;

            retval = select(sd+1, &rfds, NULL, NULL, &tv);
            /* Don't rely on the value of tv now! */

            if (retval == -1)
                perror("select()");
            else if (retval != 0)
            {
                printf("Data is available now.\n");
                /* FD_ISSET(0, &rfds) will be true. */

                cliLen = sizeof(cliAddr);
                int n;
                if((n = recvfrom(sd, msg, MAX_MSG, 0, (struct sockaddr *) &cliAddr,&cliLen)) == -1)
                    perror("Some bullshit happened");
                msg[n] = '\0';

                printf("Client IP:port is: %s:%d", inet_ntoa(cliAddr.sin_addr), (int) ntohs(cliAddr.sin_port));
            } else
            {printf("no data.\n");}
        }
        exit(0);
    }
    return 0;
}

可能的解决方案我还没有尝试过: 也许我不应该使用组播组来发送客户端->服务器的信息,而应该使用另一种类型的连接?这只是我的猜测。我知道你们不喜欢为别人做工作。

我已经花了几个小时处理这个“简单”的问题,并尝试阅读了所有可能的资料,包括这个问题,看起来非常相似,但我还没有找到任何解决办法。我完全无从下手。

2个回答

1

以下是两点建议:

  • 如果您的通信仅限于本地(在代码中使用127.0.0.1),则无需使用组播。
  • 多播组是由网络接口而不是进程加入的。一旦一个网络接口已经加入了一个多播接口,同一网络接口再次加入相同的多播组将不会发生任何变化。即使是同一主机上的另一个进程请求第二个加入。

整个东西是为了学习目的而进行的模拟,但感谢您指出第一项。我会就第二项再与您联系。还不确定它的含义。 - Ren

1
如果客户端发送到多播组,服务器希望读取该消息,那么服务器也需要加入多播组。
这并不是一个好的解决方案,因为所有其他客户端成员也将收到该多播消息。
更合理的做法是客户端首先从服务器接收多播消息,然后响应服务器的地址,该地址通过recvfrom()的结果参数提供。

在您提出的第二种解决方案中,客户端会使用sendto()方法发送数据吗? - Ren
1
使用 sendto()sendmsg() - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接