views:

950

answers:

5

I am currently using select loop to manage sockets in a proxy. One of the requirements of this proxy is that if the proxy sends a message to the outside server and does not get a response in a certain time, the proxy should close that socket and try to connect to a secondary server. The closing happens in a separate thread, while the select thread blocks waiting for activity.

I am having trouble figuring out how to detect that this socket closed specifically, so that I can handle the failure. If I call close() in the other thread, I get an EBADF, but I can't tell which socket closed. I tried to detect the socket through the exception fdset, thinking it would contain the closed socket, but I'm not getting anything returned there. I have also heard calling shutdown() will send a FIN to the server and receive a FIN back, so that I can close it; but the whole point is me trying to close this as a result of not getting a response within the timeout period, so I cant do that, either.

If my assumptions here are wrong, let me know. Any ideas would be appreciated.

EDIT: In response to the suggestions about using select time out: I need to do the closing asynchronously, because the client connecting to the proxy will time out and I can't wait around for the select to be polled. This would only work if I made the select time out very small, which would then constantly be polling and wasting resources which I don't want.

A: 

Use a timeout for the select, and if the read-ready/write-ready/had-error sequences are all empty (w.r.t that socket), check if it was closed.

shikhar
+1  A: 

It's hard to comment when seeing only a small part of the elephant but maybe you are over complicating things?

Presumably you have some structure to keep track of each socket and its info (like time left to receive a reply). You can change the select() loop to use a timeout. Within it check whether it is time to close the socket. Do what you need to do for the close and don't add it to the fd sets the next time around.

Duck
+2  A: 

Generally I just mark the socket for closing in the other thread, and then when select() returns from activity or timeout, I run a cleanup pass and close out all dead connections and update the fd_set. Doing it any other way opens you up to race conditions where you gave up on the connection, just as select() finally recognized some data for it, then you close it, but the other thread tries to process the data that was detected and gets upset to find the connection closed.

Oh, and poll() is generally better than select() in terms of not having to copy as much data around.

woolstar
Agreed-- perform the close() in the select() thread. If you need to have another thread detect the timeout, send the select() thread a message through a pipe (which you can put in the select set) instead of calling close() directly.
mark4o
A: 

How to cope with EBADF on select():

int fopts = 0;
for (int i = 0; i < num_clients; ++i) {
    if (fcntl(client[i].fd, F_GETFL, &fopts) < 0) {
        // call close(), FD_CLR(), and remove i'th element from client list
    }
}

This code assumes you have an array of client structures which have "fd" members for the socket descriptor. The fcntl() call checks whether the socket is still "alive", and if not, we do what we have to to remove the dead socket and its associated client info.

Warren Young
A: 

If you use poll(2) as suggested in other answers, you can use the POLLNVAL status, which is essentially EBADF, but on a per-file-descriptor basis, not on the whole system call as it is for select(2).

camh