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

时间:2012-09-02 22:57:05

标签: c sockets udp

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

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

  1. 在主机A上,侦听器启动并绑定到端口12345,并阻塞recvfrom
  2. 在主机A上,Sender将数据报发送到主机B上运行的服务,将源地址和端口设置为主机A,端口12345。
  3. 主机B上的服务向主机A端口12345发送响应。
  4. Listener接听回应。
  5. 通过绑定来设置源地址和端口。所以我需要Sender和Listener绑定到同一个端口。在两者中设置SO_REUSEADDR都允许这样做。请注意,我这里没有使用多播。

    但是听众并没有可靠地回答这些回复。我观察到两个例外:

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

    或者,如果Sender首先启动并在Listener之前绑定,那么听众将会收到回复。

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

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

    其他几点:

    • 每个过程都应该独立启动,并且能够重新启动而不会干扰另一个过程。所以我认为我不能打开一个套接字并产生另一个套接字。
    • 我不需要从两个进程接收数据包。一个进程只发送,另一个进程只接收。
    • 理想情况下,该解决方案具有足够的便携性,可以在常见的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,但没有完全解释它在我的情况下是如何工作的。

2 个答案:

答案 0 :(得分:0)

  

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

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

答案 1 :(得分:-1)

1:您可以从B

上的不同端口发送
A binds 12345 sends to B:12345

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

2:您可以使用带有原始套接字的虚假后台地址构建数据包

第一种解决方案更好