views:

1002

answers:

2

How to flush Input Buffer (if such thing exists at all) of an UDP Socket in C ?

I'm working on an embedded Linux environment and using C to create some native application. There are several of these embedded machines on the same network, and when an event occurs on one of them (lets call it the WHISTLE-BLOWER), WHISTLE-BLOWER should send a network message to the network broadcast address, so that all machines on the network (including the WHISTLE-BLOWER) knows about the event and executes some actions according to it. I'm using UDP socket by the way...

Here's the pseudo-code of it (i am unable to post my exact code here since; i don't have access to it at this moment but; I'll be posting it tomorrow if necessary...) :

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

The way I expect and want to work this code is:

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

The way it actually works is:

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: The packets/messages/UDP Broadcasts sent to an already binded socket, whoose parent thread is sleeping at the delivery-moment; are somehow queued/buffered and delivered at the next 'recvfrom' call on the said socket.

I want those UDP broadcasts to be ignored so; I was thinking of flushing the receive buffer (obviously not the one i'm giving as parameter to the recvfrom method) if it exists before calling recvfrom. How can I do that? or what path should I follow?

+2  A: 

Please note that the notion of "flushing" only applies to output. A flush empties the buffer and ensures everything in it was sent to its destination. Regarding an input buffer, the data is already at its destination. Input buffers can be read from or cleared out, but not "flushed".

If you just want to make sure you have read everything in the input buffer, what you are looking for is a non-blocking read operation. If you try it and there's no input, it should return an error.

bta
Point of clarification: at the point you want to "flush the input buffer," do a looping non-blocking read. If there's data, ignore it and keep reading. If there is no data (where @bta says "return an error") you ignore the return "error" and your buffer is "flushed."
mpez0
My idea and positioning of the input/receive buffer clearing/flushing may be wrong but; what I basically want to do is ignore any messages sent during that sleep period.My code currently uses/depends on blocking reads at the moment; to do a-looping-non-blocking-read as a cleaner, I'd need to set my sockets back and forth between non blocking and blocking... right? Because, I don't want and can't afford my network listener to poll the socket continously.
kramer
Do you have a command like `select` (http://linux.die.net/man/2/select) available? If you do, you can use `select` to determine if any input is available and only call your blocking read function when you need to. If you have such a function available, I would recommend using `select` and non-blocking I/O (blocking read functions are sometimes implemented internally as `select` + non-blocking read anyway). This would give you more flexibility and control than blocking I/O.
bta
If you only want to ignore I/O during a sleep period, then set a condition variable when the sleep period starts and clear it when sleep ends. Your I/O thread can check the condvar whenever your blocking read returns, and if the condvar is set then it can discard the data and wait for the next input. To do this using the code you provided, you would create a new thread that was a simple I/O loop, and where you called `recvfrom` you would instead call a function that would retrieve data from the I/O loop (keep them on an internal queue, for instance).
bta
"If you only want to ignore I/O during a sleep period..." Exactly ! But; the thing is my sleep-period and I/O thread are the same threads (see: networkListenerFunction in OP). Although seperating them to 2 threads and implementing your idea is possible; I'm curious how this compares to the 'destruct-socket-at-endOfWhile-recreate-at-startOfWhile' idea from Nikolai N Fetissov's answer...
kramer
+1  A: 

A socket has a single receive buffer inside the TCP/IP stack. It's essentially a FIFO of the datagrams received. TCP and UDP handle that queue differently though. When you call recv(2) on a UDP socket you dequeue a single datagram from that buffer. TCP arranges the datagrams into a stream of bytes according to the sequence numbers. When receive buffer overflows the datagram is dropped by the stack. TCP tries to resend in this case. UDP doesn't. There's no explicit "flush" function for the receive buffer other then reading the socket or closing it.

Edit:

You have an inherent race condition in your application and it looks like you are trying to solve it with a wrong tool (TCP/IP stack). What I think you should be doing is defining a clean state machine for the app. Handle the events that make sense at current state, ignore events that don't.

One other thing to look at is using multicast instead of broadcast. It's a bit more involved but you would have more control of the "subscriptions" by joining/leaving multicast groups.

Nikolai N Fetissov
Then, would it be a good idea to close/destruct the socket at the end of while loop, and re-create/bind it at the start of loop? It just seems too much work to put in an theoritcally-endless-loop, for a machine with 200MHz processor... or isn't it?
kramer
That depends. If you close the socket, then while it is closed any senders on the other end are liable to get ICMP error messages back for their UDP sends to that port. If that won't hose anything, go for it.
T.E.D.
All machines are sending messages to the network broadcast address so; I guess they won't be getting those error messages since the send_address is not specifically defined...?
kramer
Closing and re-opening the port doesn't sound like the way to go here. When you close the port, you open up the opportunity that something else could open it before you do. Plus, the open/close operations will likely incur a lot of overhead. Not to mention, this will likely cause problems for anything remote devices connected to the port. Once you've opened the port, don't close it until you are completely finished with it.
bta