0

I have a server with 2 connections SOCKET which is connected with clients and I set this server is non-blocking mode which don't stop when sending or recieving message. I want to set time out for a SOCKET of each connections, but if I use the following code:

 void getMessage(SOCKET connectedSocket, int time){ 
    string error = R_ERROR;
    // Using select in winsock
    fd_set  set;
    timeval tm;

    FD_ZERO(&set);
    FD_SET(connectedSocket, &set);

    tm.tv_sec = time; // time 
    tm.tv_usec = 0; // 0 millis
    switch (select(connectedSocket, &set, 0, 0, &tm))
    {
    case 0:
        // timeout
        this->disconnect();
        break;
    case 1:
        // Can recieve some data here
        return this->recvMessage();
        break;
    default:
        // error - handle appropriately.
        break;
    }
return error;
}

My server is not none-blocking mode any more! I have to wait until the end of 1st connection's time out to get message from the 2nd connection! That's not what I expect! So, is there any way to set time out for non-blocking mode? Or I have to handle it myself?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Kingfisher Phuoc
  • 8,052
  • 9
  • 46
  • 86
  • 1
    You might have to refactor the code so you do `select` on both sockets, and if `select` doesn't time-out check which socket are ready (remember that it could be both) and call the appropriate receive method. – Some programmer dude Jun 13 '12 at 05:23
  • +1 on what Joachim said. Your core server loop should be calling select() on all sockets and then you have an object (class instance) associated with each socket. The "Read" method on each class instance reads as as much as it can from each socket and processes what it can as well. Possibly doing "sends" asynchrionous as well. – selbie Jun 13 '12 at 05:35
  • Since you have two different threads, I wouldn't bother with non-blocking I/O and select. Just let each thread do blocking I/O. If you need a timeout on recv, use [this solution](http://stackoverflow.com/questions/2876024/linux-is-here-a-read-or-recv-from-socket-with-timeout) – jxh Jun 13 '12 at 05:52
  • I called `getMessage` function in both connections `SOCKET`, but it still have to wait! – Kingfisher Phuoc Jun 13 '12 at 06:03

1 Answers1

4

select is a demultiplexing mechanism. While you are using it to determine when data is ready on a single socket or timeout, it was actually designed to return data ready status on many sockets (hence the fd_set). Conceptually, it is the same with poll, epoll and kqueue. Combined with non-blocking I/O, these mechanisms provide an application writer with the tools to implement a single threaded concurrent server.

In my opinion, your application does not need that kind of power. Your application will only be handling two connections, and you are already using one thread per connection. I believe leaving the socket in blocking I/O mode is more appropriate.

If you insist on non-blocking mode, my suggestion is to replace the select call with something else. Since what you want from select is an indication of read readiness or timeout for a single socket, you can achieve a similar effect with recv passed with appropriate parameters and with the appropriate timeout set on the socket.

tm.tv_sec = time;
tm.tv_usec = 0;
setsockopt(connectedSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tm, sizeof(tm));
char c;
swtich (recv(connectedSocket, &c, 1, MSG_PEEK|MSG_WAITALL)) {
case -1:
    if (errno == EAGAIN) {
        // handle timeout ...
    } else {
        // handle other error ...
    }
    break;
case 0: // FALLTHROUGH
default:
    // handle read ready ...
    break;
}

From man recv:

MSG_PEEK -- This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.

MSG_WAITALL (since Linux 2.2) -- This flag requests that the operation block until the full request is satisfied. However, the call may still return less data than requested if a signal is caught, an error or disconnect occurs, or the next data to be received is of a different type than that returned.

As to why select is behaving in the way you observed. While the select call is thread-safe, it is likely fully guarded against reentrancy. So, one thread's call to select will only come in after another thread's call completes (the calls to select are serialized). This is inline with its function as a demultiplexer. It's purpose is to serve as a single arbiter for which connections are ready. As such, it wants to be controlled by a single thread.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Can you explain your parameters in your recv function? – Kingfisher Phuoc Jun 13 '12 at 06:50
  • @user315502 I used `while(true)` loop, in this `while` i `accept` connections from client in a `for` loop and another `for` loop to `recieve` clients' messages! With those code, where i put `setsockopt` and how i recieve message from your `recv`, becuase in your `recv` i see that it only recieve 1 char!! am i wrong? – Kingfisher Phuoc Jun 13 '12 at 07:12
  • @Kingfisher, the `recv` code I am presenting is meant to be as a kind of a replacement for your `select` call, so just try to call your `recvMessage` and `disconnect` methods from the appropriate places within the code I provided. Please read what `MSG_PEEK` does more carefully. Regards. – jxh Jun 13 '12 at 07:15
  • @user315502: in your `case -1`, what is `error`? are you mean `error = setsockopt`? – Kingfisher Phuoc Jun 13 '12 at 07:35
  • @Kingfisher: `errno` is the way error codes are reported on UNIX systems. If `recv` returns `-1`, some error occurured, consulting `errno` is how to find out reason for error. – jxh Jun 13 '12 at 14:40