c - 另一个进程是否接收到对 UDP 数据报的响应?

标签 c sockets udp

我正在尝试实现一个基本的 UDP 协议(protocol),其中发送方向服务发送 UDP 数据报,然后服务使用传入数据报的源地址和源端口发回响应数据报。

通常您会让发件人也监听该端口上的响应。但我希望响应由也在该主机上运行的单独程序(监听器)接收。所以:

  1. 在主机 A 上,监听器启动并绑定(bind)到端口 12345,并在 recvfrom 上阻塞。
  2. 在主机A上,Sender向运行在主机B上的Service发送数据报,设置源地址和端口为主机A,端口12345。
  3. 主机 B 上的服务向主机 A 端口 12345 发送响应。
  4. 响应由 Listener 接收。

设置源地址和端口是通过绑定(bind)它们来完成的。所以我需要 Sender 和 Listener 都绑定(bind)到同一个端口。在两者中设置 SO_REUSEADDR 允许这样做。请注意,我在这里没有使用多播。

但是响应并没有被 Listener 可靠地接收到。我观察到有两个异常(exception):

我发现如果发送者在发送第一个数据报后立即关闭套接字,那么响应将到达监听者。

或者,如果发送器先启动并在监听器之前绑定(bind),响应将被监听器接收。

我一直在研究来自 Internet 的示例,但没有找到明确描述应该发生什么的文档。但是我看到的一些地方已经暗示,对于单播,只有最近绑定(bind)到端口的进程才会接收发送给它的数据报。

我的问题是,我可以发送 UDP 数据报,以便另一个进程接收响应(使用源地址和端口发送)吗?如果上述过程无法工作,有没有办法在不绑定(bind)到该端口的情况下在传出数据报上设置源信息?

其他几点:

  • 每个进程都应该独立启动,并且能够在不干扰其他进程的情况下重新启动。所以我不认为我可以让一个打开套接字并生成另一个。
  • 我不需要从两个进程接收数据包。一个进程只发送,另一个只接收。
  • 理想情况下,该解决方案具有足够的可移植性,可以在常见的 Unix 和 Windows 上运行。
  • 最后,如果根本不可能,那么我将回过头来使用单个进程来执行这两个功能。我对此并没有太大压力,但如果可能的话,我有兴趣去做。 :-)

网络代码如下...

发件人代码

void run(Options *options)
{
    struct sockaddr_in si_me, si_other;
    int s;
    socklen_t slen = sizeof(si_other);
    int reuse = 1;
    struct hostent *he;

    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        die("socket");

    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
        die("setsockopt");

    // Bind to the "listen port", so that outgoing datagrams have the correct source information
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(options->listen_port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) != 0)
        die("bind");

    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(options->service_port);

    if (!(he = gethostbyname2(options->service_host, AF_INET)))
        die("gethostbyname2");

    memmove(&si_other.sin_addr.s_addr, he->h_addr, he->h_length);

    while (1)
    {
        int len;
        char *buf;

        // Create outgoing message in buf
        ...

        if (sendto(s, buf, len, 0, (struct sockaddr *) &si_other, slen) == -1)
            die("sendto");
    }
    close(s);
}

监听代码

static void run(Options *options)
{
    struct sockaddr_in si_me, si_other;
    int s;
    socklen_t slen = sizeof(si_other);
    char buf[BUFLEN];
    int reuse = 1;

    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        die("socket");

    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
        die("setsockopt");

    // Bind to the same "listen port" to pick up responses to datagrams sent by Sender
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(options->listen_port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) == -1)
        die("bind");

    while (1)
    {
        int nr;

        nr = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen);
        if (nr == -1)
            die("recvfrom");

        // Process the received message
        ...
    }

    close(s);
}

一个相关的问题是Using netcat to send a UDP packet without binding ,其中一个答案似乎表明应该可以使用 SO_SOCKADDR,但没有完全解释在我的情况下它是如何工作的。

最佳答案

is there a way to set the source information on an outgoing datagram without binding to that port?

没有可移植的方式。使用 IP_PKTINFO 的 Linux 解决方案是对 How to re bind a udp socket in Linux 的回答.

关于c - 另一个进程是否接收到对 UDP 数据报的响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12240424/

相关文章:

java - android 和 UDP 数据包的奇怪行为

c - 从文本文件读取后,结果显示不正确

C: vsprintf 覆盖数组

java - java游戏使用TCP和UDP

linux - UDP 在同一台机器上广播?

Java pcap 解析器解析为 ByteBuffer

c - 使用 gcc 包含目录未正确链接

c - 我想知道是否有办法将 OpenMP 中的特定线程分配给特定的 CPU 核心?

java - Maya 和 Android 应用程序 - 通过 TCP 将 Mel 命令发送到 Maya

c# - 从 C# 客户端向 C 服务器发送消息