Linux 如何在本地使用 UDP 套接字广播消息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11135801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:00:05  来源:igfitidea点击:

How to broadcast Message using UDP sockets locally?

clinuxsocketsipc

提问by Yuvi

I want to broadcast messages locally to many application. For that I thought UDP sockets is the best IPC, correct me if I am worng.

我想在本地向许多应用程序广播消息。为此,我认为 UDP 套接字是最好的 IPC,如果我感到厌烦,请纠正我。

For this I am using the following codes:

为此,我使用以下代码:

For broadcast:

对于广播:

/*
** broadcaster.c -- a datagram "client" that can broadcast
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT 4950    // the port users will be connecting to

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in their_addr; // connector's address information
    struct hostent *he;
    int numbytes;
    int broadcast = 1;
    //char broadcast = '1'; // if that doesn't work, try this

    if (argc != 3) {
        fprintf(stderr,"usage: broadcaster hostname message\n");
        exit(1);
    }

    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    // this call is what allows broadcast packets to be sent:
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
        sizeof broadcast) == -1) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }

    their_addr.sin_family = AF_UNIX;     // host byte order
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(their_addr.sin_zero, '
/*
** listener.c -- a datagram sockets "server" demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define MYPORT "4950"    // the port users will be connecting to

#define MAXBUFLEN 100

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    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];
    int optval = 1;

    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;
    }

    // loop through all the results and bind to the first we can
    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(setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &optval, sizeof optval) != 0)
        {
            perror("listener: setsockopt");
            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);

    printf("listener: waiting to recvfrom...\n");

    addr_len = sizeof their_addr;
    if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
        (struct sockaddr *)&their_addr, &addr_len)) == -1) {
        perror("recvfrom");
        exit(1);
    }

    printf("listener: got packet from %s\n",
        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s));
    printf("listener: packet is %d bytes long\n", numbytes);
    buf[numbytes] = '##代码##';
    printf("listener: packet contains \"%s\"\n", buf);

    close(sockfd);

    return 0;
}
', sizeof their_addr.sin_zero); if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0, (struct sockaddr *)&their_addr, sizeof their_addr)) == -1) { perror("sendto"); exit(1); } printf("sent %d bytes to %s\n", numbytes, inet_ntoa(their_addr.sin_addr)); close(sockfd); return 0; }

And to listen :

并听:

##代码##

The problem is that I have to pass IP like this 192.168.1.255 but in the real scenario there may not be eth0 interface, there will be only loopback. Then how can I achieve this?

问题是我必须像这样传递IP 192.168.1.255 但在实际场景中可能没有eth0接口,只会有环回。那我怎样才能做到这一点?

采纳答案by Some programmer dude

The server should not be bound to an address you get from getaddrinfo, instead it should be bound to 127.255.255.255(for the loopback interface).

服务器不应绑定到您从中获得的地址getaddrinfo,而应绑定到127.255.255.255(对于环回接口)。

For a ready-made example of broadcast server/client see http://www.ccplusplus.com/2011/09/udp-broadcast-client-server-example.html

有关广播服务器/客户端的现成示例,请参阅http://www.ccplusplus.com/2011/09/udp-broadcast-client-server-example.html

回答by Maxim Egorushkin

Unix domain sockets don't support multi-/broadcasting.

Unix 域套接字不支持多/广播。

You can broadcast on the local interface 127.0.0.1.

您可以在本地接口 127.0.0.1 上广播。

回答by Cameron

While the original question doesn't explicitly say so, I believe the original asker wanted to 'broadcast' to multiple applications running on the same operating-system instance (same computer to old timers).

虽然最初的问题没有明确说明,但我相信最初的提问者想要“广播”到在同一操作系统实例上运行的多个应用程序(同一台计算机到旧计时器)。

This is supported by the use of 'SO_REUSEADDR' in the listener example, and followup comments by Yuvi, and finally a suggestion to use IP multicast.

这是通过在侦听器示例中使用“SO_REUSEADDR”以及 Yuvi 的后续评论以及最后使用 IP 多播的建议来支持的。

The original question should be clarified.

应该澄清最初的问题。

I believe packet distribution with multiple-binders on a single UDP port varies between operating systems when using SO_REUSEADDR. My experience on recent Windows, is that a single 'binder' is exclusively given all packets until she releases her bind, at which time, another binder is chosen and presented all received packets, until she releases, and so on...

我相信在使用 SO_REUSEADDR 时,单个 UDP 端口上具有多个绑定器的数据包分发会因操作系统而异。我在最近的 Windows 上的经验是,在她释放绑定之前,一个单一的“绑定器”被专门分配给所有数据包,此时,另一个绑定器被选择并呈现所有收到的数据包,直到她释放,依此类推......

This apparently differs from recent Linux kernels, as explained in this link: https://stackoverflow.com/a/14388707/86375That page appears to claim Linux will round-robin receives packets between multiple binders.

这显然不同于最近的 Linux 内核,如此链接中所述:https: //stackoverflow.com/a/14388707/86375该页面似乎声称 Linux 将循环接收多个绑定器之间的数据包。

The end-result, if you hope to send-to-many using a single sent-datagrams as the original poster did, and you attempt to use IP unicast, not IP multicast, you may be disappointed. (My experience, and the link above show you can multi-bind, but that doesn't imply multi-delivery of received datagrams, neither on Linux or Windows)

最终结果是,如果您希望像原始发布者那样使用单个发送的数据报进行多方发送,并且您尝试使用 IP 单播,而不是 IP 多播,那么您可能会感到失望。(我的经验和上面的链接显示您可以进行多重绑定,但这并不意味着在 Linux 或 Windows 上都可以多重传送接收到的数据报)

The original poster should have tried using multicast.

原始海报应该尝试使用多播。