문제

I am implementing a kind of IP finder for a particular type of network multimedia device. I want to find out all the alive devices of that type in the LAN, with their IP address and other details.

The device has its own way of device discovery.

It works as follows: A client sends a broadcast request over the LAN via UDP.
The destination port number is fixed.
In reply, all the servers in the LAN that understand the format of this request will respond to this request providing information about themselves.

I am broadcasting the UDP request message using sendto().

Now my problem is that I don't know how many devices (i.e.servers) will respond to the request.

How many times will I have to call recvfrom()?
When will I come to know that I have handled the response from all the devices?
Or in general, is recvfrom() the right choice for receiving response from multiple servers?
Is there any better (or CORRECT if I am wrong here) way of accomplishing the same?

I am programming in C/C++, planning to code for both Windows and Linux.
Many thanks in advance.

Edit: So with the help of all the network programming wizards out here, I have found the solution to my problem :)
select() is just the thing for me...
Thanks a lot to all of you who took out time to help me

도움이 되었습니까?

해결책

How many times will I have to call recvfrom()? When will I come to know that I have handled the response from all the devices/servers?

If you don't know the number of devices/servers, you cannot know how many times you will need to call recvfrom() or when you've handled all the responses.

You might consider using a select() loop (until timeout) and call recvfrom() when data is available to read. This might be in the main thread or a separate thread.

If the data arrives faster than it can be processed, you will lose datagrams. This will depend largely on the speed that the data is parsed and stored after it is received. If processing the data is an intensive operation, it may be necessary to do the processing in a separate thread or store the data until the receive loop times out and then proceed with processing it.

Since UDP is unreliable, looping to rebroadcast a few times should help account for some of the loss and the processing should account for duplicates.

The following pseudocode is how I might approach the problem:


/* get socket to receive responses */
sd = socket( ... );

do
{
    /* set receive timeout */
    timeout.tv_sec = 5;     

    /* broadcast request */
    sendto( ... );

    /* wait for responses (or timeout) */
    while(select(sd+1, &readfds, NULL, NULL, &timeout) > 0)
    {
        /* receive the response */
        recvfrom( ... );

        /* process the response (or queue for another thread / later processing) */
        ...

        /* reset receive timeout */
        timeout.tv_sec = 5; 
    }

    /* process any response queued for later (and not another thread) */

} while (necessary);

Or in general, is recvfrom() the right choice for receiving response from multiple servers?

recvfrom() is commonly used with connectionless-mode sockets because it permits the application to retrieve the source address of received data.

다른 팁

Use a select(2)/poll(2) with a timeout in a loop, decrementing the timeout every time you get a response from a device. You'd have to come up with appropriate timeout yourself.

Alternatively, if you are able to recognize/parse the discovery response message, just add the device to the list upon receiving such message.

You will probably have to deal with timeouts anyway for when devices register but fail at a later point.

If you don't know how many servers are going to respond, then you don't know how many times you have to call recvfrom(). I would probably handle this with a select() loop with a suitable timeout, something like the following, which is completely untested and probably full of stupid bugs:

/* create and bind socket */

fd_set fds;
struct timeval tv;

tv.tv_sec = 2; 
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock, &fds);
int ret;

while((ret = select(sock + 1, &fds, NULL, NULL, &tv)) > 0) {
    char buf[BUFLEN];
    struct sockaddr addr;

    if(recvfrom(sock, buf, BUFLEN, MSG_DONTWAIT, &addr, sizeof(struct sockaddr)) > 0) {
        /* handle response */
    } else {
        /* handle error */
    }        
}
if(ret < 0) {
    /* handle error */
} else {
    /* select() timed out; we're theoretically done */
}

This will keep calling recvfrom() until no reply has been received for 2 seconds, which of course means that it will block for a minimum of 2 seconds. Depending on the underlying protocol, you can probably get away with a much shorter timeout; indeed you can decrease it on each response. Some testing and tuning will be required to find the optimum configuration. You don't need to worry about servers responding at the same time; the Ethernet layer will handle that.

You can't know. Its unknowable.

Presumably, from your description: I want to find out all the alive devices, the devices can transition from dead to alive and back again any time they want to. This means that you will have to poll continuously: ie send the broadcast request every few seconds (but not too often) and see who responds.

If I've got this right, that UDP is inherently unreliable, you are going to have to retro-fit some reliability on top of UDP:

  • Send the broadcast out every few seconds, since devices may not receive it every time.
  • You may not receive their replies, but you might the next time.
  • A reply confirms that a device is alive.
  • Wait for 'n' non-responses before declaring a device dead.

Do you know the maximum number of possible devices? If you do you may find that you have to call recvfrom() that many times.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top