使用单个套接字接收单播和多播数据

时间:2017-05-10 15:51:29

标签: c sockets

我正在开发基于网络的应用程序,其中我想要接收单播和多播数据。所以我想知道是否可以使用单个套接字接收单播数据和组播数据。这是我的代码,截至目前我只能接收单播数据,但不能接收多播数据。

void* rec(void* t)
{
    printf("Rec Thread created\n");
    char buf[200];
    struct sockaddr_in soc;
    soc.sin_family=AF_INET;
    soc.sin_addr.s_addr=inet_addr("123.1.2.3");
    soc.sin_port=htons(1234);

    long sock_fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

    bind(sock_fd,(struct sockaddr *)&soc,sizeof(sockaddr_in));

    struct ip_mreq mreq;
    memset(&mreq,0,sizeof(struct ip_mreq));
    mreq.imr_multiaddr.s_addr=inet_addr("235.5.5.5");
    mreq.imr_interface.s_addr=inet_addr("123.1.2.3");

    setsockopt(sock_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) 

    while(true)
    {
        memset(buf,0,sizeof(buf));
        long size=recvfrom(sock_fd,buf,200,0,NULL,NULL);
        if(size>0)
        {
            printf("recvd %s\n",buf);
        }
        else
        {
            perror("Error receving data");
        }
    }
}

int main(int argc,char* argv[])
{
    pthread_create(&pt,NULL,rec,NULL);
    sleep(2);
    char buf[]="Hello World!;
    struct sockaddr_in soc;
    soc.sin_family=AF_INET;
    soc.sin_addr.s_addr=inet_addr("123.1.2.3");
    soc.sin_port=htons(1234);

    long sock_fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

    sendto(sock_fd,buf,sizeof(buf),0,(struct sockaddr *)&soc,sizeof(sockaddr_in));

    struct sockaddr_in msoc;
    msoc.sin_family=AF_INET;
    msoc.sin_addr.s_addr=inet_addr("235.5.5.5");
    msoc.sin_port=htons(1234);

    char mbuf[]="Hi All!";
    sendto(sock_fd,mbuf,sizeof(mbuf),0,(struct sockaddr *)&msoc,sizeof(sockaddr_in));

    return 0;
}

2 个答案:

答案 0 :(得分:0)

有可能。或者至少'有点'可能。

请考虑以下代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(void) {
    struct sockaddr_in vSSocket;
    vSSocket.sin_family = AF_INET;
    vSSocket.sin_addr.s_addr = INADDR_ANY;
    vSSocket.sin_port = htons(1234);

    int vSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (vSocket < 0) {
        perror("socket");
        return 1;
    }

    struct ip_mreq vReq;
    vReq.imr_multiaddr.s_addr = inet_addr("235.5.5.5");
    vReq.imr_interface.s_addr = inet_addr("123.1.2.3");
    if (setsockopt(vSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &vReq, sizeof(vReq)) < 0) {
        perror("setsockopt 2");
        return 1;
    }
    if (bind(vSocket, (struct sockaddr *)&vSSocket, sizeof(vSSocket)) == -1) {
        perror("bind");
        return 1;
    }
    ssize_t r;
    char vBuf[256];
    while (1) {
        r = recv(vSocket, vBuf, sizeof(vBuf), 0);
        if (r < 0) {
            perror("recv");
            break;
        }
        if (memcmp(vBuf, "quit", 4) == 0) {
            printf("quit\n");
            break;
        }
        write(1, vBuf, r);
    }
    return 0;
}

诀窍是:将套接字绑定到INADDR_ANY地址。因此,它会监听您机器上的每个界面。此外,具有地址123.1.2.3的接口加入多播组235.5.5.5,因此它将接收该地址的任何数据包。

您可以使用精湛的socat工具测试此解决方案。 要发送到多播地址:

socat STDIO UDP4-DATAGRAM:235.5.5.5:1234

发送到单播地址:

socat STDIO UDP4-DATAGRAM:123.1.2.3:1234

程序应输出您发送给它的任何内容,并在收到以“退出”开头的数据包后退出。

答案 1 :(得分:0)

可以。 nsilent22拥有前90%。

  • 将套接字绑定到INADDR_ANY
  • 设置IP_ADD_MEMBERSHIP(ipv6:IPV6_JOIN_GROUP
  • 如果需要发送,请设置IP_MULTICAST_IF(ipv6:IPV6_MULTICAST_IF)来设置多播数据包的输出接口。

这将为您提供一个套接字,该套接字侦听多播组,但也接收到计算机的所有单播流量。要将其限制为单个界面,您有两个选择:

  • 设置IP_PKTINFO(ipv6:IPV6_RECVPKTINFO),使用recvmsg接收数据,并根据ipi_ifindex结构的in_pktinfo成员(ipv6:{{ ipi6_ifindex的1}}位成员。
  • 设置(特定于Linux的)套接字选项struct in6_pktinfo(需要root特权)

这样,您将获得一个套接字,该套接字从选定的多播组接收多播流量以及所有单播流量,但仅限于单个接口。

来源:ip,ipv6和套接字的联机帮助页。