首页 文章

是否有其他进程获取UDP数据报的响应?

提问于
浏览
1

我正在尝试实现一个基本的UDP协议,其中发送者将UDP数据报发送到服务,然后服务使用来自传入数据报的源地址和源端口发回响应数据报 .

通常你会让发件人也听取该端口的响应 . 但是我希望响应能够通过在该主机上运行的单独程序(Listener)来获取 . 所以:

  • 在主机A上,监听器启动并绑定到端口12345,并在 recvfrom 上阻塞 .

  • 在主机A上,发送方将数据报发送到主机B上运行的服务,将源地址和端口设置为主机A,端口12345 .

  • 主机B上的服务向主机A端口12345发送响应 .

  • 侦听器拾取响应 .

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

但是,Listener并没有可靠地回复这些回复 . 我观察到两个例外:

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

或者,如果发件人首先启动并在侦听器之前绑定,则侦听器将获取响应 .

我一直在从互联网上的例子开始工作,但没有找到明确描述应该发生的事情的文档 . 但是我见过的一些地方暗示,对于Unicast,只有绑定到端口的最新进程才会收到发送给它的数据报 .

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

其他几点:

  • 每个进程应独立启动,并且能够重新启动而不会干扰另一个进程 . 所以我认为我不能打开套接字并产生另一个套接字 .

  • 我不需要从两个进程接收数据包 . 一个进程只发送,另一个只接收 .

  • 理想情况下,该解决方案的可移植性足以在常见的Unix和Windows上运行 .

  • 最后,如果它's simply not possible then I' ll回退到使用单个进程来执行这两个功能 . 我对它没有太多的压力,但如果有可能的话,我有兴趣这么做 . :-)

网络代码如下......

Sender code

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

Listener code

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,但没有完全解释它在我的情况下如何工作 .

2 回答

  • -1

    1:您可以从B上的不同端口发送

    A binds 12345 sends to B:12345
    
    B:12345 - process 1 - recv 
    B:12346 - process 2 - send to A:12345
    

    2:您可以使用原始套接字构造带有伪造后备地址的数据包

    第一种解决方案更好

  • 0

    有没有办法在传出数据报上设置源信息而不绑定到该端口?

    没有便携的方式 . 使用 IP_PKTINFO 的Linux解决方案是How to re bind a udp socket in Linux的答案 .

相关问题