【问题标题】:How to flush Input Buffer of an UDP Socket in C?如何在 C 中刷新 UDP 套接字的输入缓冲区?
【发布时间】:2010-02-19 21:56:48
【问题描述】:

如何在 C 中刷新 UDP Socket 的输入缓冲区(如果存在的话)?

我正在开发嵌入式 Linux 环境并使用 C 创建一些本机应用程序。在同一个网络上有几台这样的嵌入式机器,当其中一台发生事件时(我们称之为 WHISTLE-BLOWER),WHISTLE-BLOWER 应该向网络广播地址发送网络消息,以便所有机器上网络(包括 WHISTLE-BLOWER)知道该事件并根据它执行一些操作。 顺便说一句,我正在使用 UDP 套接字...

这是它的伪代码:

main
{
    startNetworkListenerThread( networkListenerFunction );

    while( not received any SIGTERM or such )
    {
        localEventInfo = checkIfTheLocalEventOccured();
        broadcastOnNetwork( localEventInfo );
    }
}

networkListenerFunction
{
    bindSocket;

    while( not SIGTERM )
    {
// THIS IS WHERE I WANT TO FLUSH THE RECV BUFFER...
        recv_data = recvfrom( socket );
        if( validate recv data )
        {
            startExecuteLocalAction;
            sleep( 5 );
            stopExecuteLocalAction;
        }
    }
}

期望并希望使用此代码的方式是:

1. LOCAL_EVENT occured
2. Broadcasted LOCAL_EVENT_INFO on network
3. All machines received EVENT_INFO, including the original broadcaster
4. All machines started executing the local action, including the original broadcaster
5. All machines' network listener(thread)s are sleeping
6. Another LOCAL_EVENT2 occured
7. Since all machines' listener are sleeping, LOCAL_EVENT2 is ignored
8. All machines' network listener(thread)s are now active again
9. GO BACK TO 1 / RESTART CYCLE
RESULT = TOTAL 2 EVENTS, 1 IGNORED

它的实际工作方式是:

1. LOCAL_EVENT occured
2. Broadcasted LOCAL_EVENT_INFO on network
3. All machines received EVENT_INFO, including the original broadcaster
4. All machines started executing the local action, including the original broadcaster
5. All machines' network listener(thread)s are sleeping
6. Another LOCAL_EVENT2 occured
7. Eventhough all machines' listener are sleeping; LOCAL_EVENT2 is queued  SOMEHOW
8. All machines' network listener(thread)s are now active again
9. All machines received EVENT_INFO2 and executed local actions again, slept and reactivated
10. GO BACK TO 1 / RESTART CYCLE
RESULT = TOTAL 2 EVENTS, 0 IGNORED

tl,dr:发送到已经绑定的套接字的数据包/消息/UDP广播,其父线程在交付时刻处于休眠状态;以某种方式排队/缓冲并在所述套接字上的下一次“recvfrom”调用中传递。

我希望忽略那些 UDP 广播,所以我想在调用 recvfrom 之前刷新接收缓冲区(显然不是我作为参数提供给 recvfrom 方法的那个)。我怎样才能做到这一点?或者我应该走哪条路?

【问题讨论】:

    标签: c sockets embedded udp


    【解决方案1】:

    请注意,“刷新”的概念仅适用于输出。刷新清空缓冲区并确保其中的所有内容都发送到其目的地。关于输入缓冲区,数据已经在其目的地。可以读取或清除输入缓冲区,但不能“刷新”。

    如果您只是想确保您已读取输入缓冲区中的所有内容,那么您正在寻找的是非阻塞读取操作。如果你尝试它并且没有输入,它应该返回一个错误。

    【讨论】:

    • 澄清点:在您想要“刷新输入缓冲区”的点上,执行循环非阻塞读取。如果有数据,请忽略它并继续阅读。如果没有数据(@bta 说“返回错误”),则忽略返回“错误”并且缓冲区被“刷新”。
    • 我对输入/接收缓冲区清除/刷新的想法和定位可能是错误的,但是;我基本上想做的是忽略在睡眠期间发送的任何消息。我的代码目前使用/依赖于阻塞读取;做一个循环非阻塞读取作为清洁工,我需要在非阻塞和阻塞之间来回设置我的套接字......对吗?因为,我不想也负担不起我的网络监听器不断地轮询套接字。
    • 你有类似select (linux.die.net/man/2/select) 的命令可用吗?如果你这样做了,你可以使用select 来确定是否有任何输入可用,并且只在需要时调用你的阻塞读取函数。如果您有这样的功能可用,我建议使用select 和非阻塞I/O(阻塞读取功能有时在内部实现为select + 非阻塞读取)。与阻塞 I/O 相比,这将为您提供更多的灵活性和控制力。
    • 如果只想在休眠期间忽略I/O,则在休眠开始时设置条件变量,休眠结束时清除。每当您的阻塞读取返回时,您的 I/O 线程可以检查 condvar,如果设置了 condvar,那么它可以丢弃数据并等待下一个输入。要使用您提供的代码执行此操作,您将创建一个新线程,它是一个简单的 I/O 循环,并且在您调用 recvfrom 的地方,您将调用一个从 I/O 循环中检索数据的函数(保留它们例如,在内部队列上)。
    • "如果您只想在睡眠期间忽略 I/O..." 完全正确!但;问题是我的睡眠周期和 I/O 线程是相同的线程(请参阅:OP 中的 networkListenerFunction)。尽管将它们分成 2 个线程并实现您的想法是可能的;我很好奇这与 Nikolai N Fetissov 的回答中的“destruct-socket-at-endOfWhile-recreate-at-startOfWhile”的想法相比如何......
    【解决方案2】:

    套接字在 TCP/IP 堆栈内有一个 接收缓冲区。它本质上是接收到的数据报的 FIFO。不过,TCP 和 UDP 处理该队列的方式不同。当您在 UDP 套接字上调用 recv(2) 时,您会从该缓冲区中出列单个数据报。 TCP 根据序列号将数据报排列成字节流。当接收缓冲区溢出时,数据报被堆栈丢弃。在这种情况下 TCP 会尝试重新发送。 UDP没有。除了读取套接字或关闭它之外,接收缓冲区没有明确的“刷新”功能。

    编辑:

    您的应用程序中存在固有的竞争条件,看起来您正在尝试使用错误的工具(TCP/IP 堆栈)来解决它。 我认为你应该做的是为应用定义一个干净的状态机。处理在当前状态下有意义的事件,忽略没有意义的事件。

    另一件需要注意的事情是使用多播而不是广播。它涉及更多,但您可以通过加入/离开多播组来更好地控制“订阅”。

    【讨论】:

    • 那么,在 while 循环结束时关闭/销毁套接字并在循环开始时重新创建/绑定它是一个好主意吗?对于具有 200MHz 处理器的机器来说,在理论上进行无限循环似乎工作量太大……不是吗?
    • 这取决于。如果您关闭套接字,那么当它关闭时,另一端的任何发件人都可能会收到 ICMP 错误消息,因为他们的 UDP 发送到该端口。如果这不能解决任何问题,那就去吧。
    • 所有机器都向网络广播地址发送消息so;我猜他们不会收到那些错误消息,因为 send_address 没有明确定义......?
    • 关闭并重新打开端口听起来不像这里的方法。当您关闭端口时,您打开了其他东西可以在您之前打开它的机会。另外,打开/关闭操作可能会产生大量开销。更不用说,这可能会导致连接到端口的任何远程设备出现问题。一旦你打开了端口,在你完全完成它之前不要关闭它。
    猜你喜欢
    • 2014-03-13
    • 1970-01-01
    • 2020-03-30
    • 2016-11-19
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多