VMA92
VMA92

Reputation: 479

Simple select implementation with UDP in c++

I am trying to make a UDP transfer using the select method in c. I have looked at a couple guides (Beej) and I am having trouble understanding this method. What I am trying to do is send a UDP packet from client to server and use the select method to determine whether or not the packet has been dropped. So what I need to do is use select as a timer whenever I am sending a packet from client to server. I have the following code:

SOCKET s;    
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000; //used for time interval

fd_set writefds; 
FD_ZERO(&writefds);
//FD_SET(s, &writefds); without this I get timed out

select(s, NULL, &writefds, NULL, &tv); //timer
sendto(s, szbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)); //send content

if (FD_ISSET(s, &writefds))
    cout << "sent" << endl;
else
    cout << "timed out" << endl;

This gives the time out result everytime. Does this tell me that the packet is being timed out or is this result not related to my packet transfer all together? Maybe I need to include the other flags as well? Appreciate the feedback as I am a little confused with this method.

PS: szbuffer, BUFFER_SIZE, sa_in, etc have been previously defined. It is simply my buffer that I will be sending, with the size and the address (sa_in) I am sending it to. I was successful in sending to the server without the select method.

Upvotes: 1

Views: 4042

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595827

There are some problems with your code.

  1. you are passing the socket value as-is to the first parameter of select(), but you actually need to pass s+1 instead if you are monitoring a single socket. The parameter must be set to the highest socket value +1 that is being monitored. On Windows, the parameter is completely ignored, but on other platforms it is actually used, so you have to specify a proper value on non-Windows platforms where a socket is just an index into an array of file descriptors. The parameter represents the highest index in that array where select() cannot access.

  2. you commented out the line of code that tells select() which socket(s) to monitor. You need to put that line back in.

  3. you are misusing select() for what you are attempting to do. You are querying the socket if it is writable (has space in its outbound buffer) before sending the packet, and then assuming that if send() succeeds then the packet was not dropped. send() merely tells you whether the packet was put into the socket's outbound buffer, not whether the packet was actually transmitted.

There is nothing in the socket API to warn you if UDP packets are being dropped. The only way to detect dropped packets is to design your protocol so that the receiver has to send back an acknowledgement of every packet received. Then you can do something more like this:

SOCKET s;    
...
if (sendto(s, szbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)) > 0)
{
    cout << "sent" << endl;

    struct timeval tv;
    // .5s is too short a time to wait for a reply, bump it up a bit higher
    //tv.tv_sec = 0;
    //tv.tv_usec = 500000;
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    fd_set readfds; 
    FD_ZERO(&readfds);
    FD_SET(s, &readfds);

    int ret = select(s+1, &readfds, NULL, NULL, &tv);
    if (ret > 0)
    {
        // socket has pending data to read
        if (recvfrom(s, rcvbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)) >= 0)
        {
            // todo: verify the packet is an acknowledgement
            // of the packet sent above and not something else...
            cout << "ack received" << endl;
        }
        else
        {
            cout << "error reading" << endl;
        }
    }
    else if (ret == 0)
    {
        cout << "timed out waiting for ack" << endl;
        // todo: resend the same packet again, or abort the transfer
    }
    else
    {
        cout << "error selecting" << endl;
    }
}
else
    cout << "error sending" << endl;

Look at Trivial FTP (TFTP) for an example of a protocol that transfers data with acks.

Upvotes: 4

Steffen Ullrich
Steffen Ullrich

Reputation: 123270

to determine whether or not the packet has been dropped.

I think you fail to understand what select is for. The only way for an application to find out if the peer received the UDP packet is if the peer responds to the packet and the application receives the response.

Upvotes: 2

Related Questions