views:

969

answers:

10

Is it possible to unlisten on a socket after you have called listen(fd, backlog)?

Edit: My mistake for not making myself clear. I'd like to be able to temporarily unlisten on the socket. Calling close() will leave the socket in the M2LS state and prevent me from reopening it (or worse, some nefarious program could bind to that socket)

Temporarily unlistening would be a way (maybe not the best way) to signal to an upstream load balancer that this app couldn't accept any more requests for the moment

+5  A: 

Close it. As I recall;

close(fd);
Matthew Scharley
+1  A: 

There is no explicit method to unlisten!

You can either close(fd) or shutdown(fd, how)

fd is the socket file descriptor you want to shutdown, and how is one of the following:

0 Further receives are disallowed

1 Further sends are disallowed

2 Further sends and receives are disallowed (like close())
Prakash
shutdown() may be allowed, but the only values I'd expect to have any effect would be 0 or 2, which would shutdown all usefulness in a listening socket anyway. close() is easier, and more than explicit enough, especially in (I'm assuming) C.
Matthew Scharley
+3  A: 

After closing the socket, your programs may still tell you that the socket is "in use", this is because of some weirdiness I don't know exactly about. But the manpage about sockets shows you there is a flag to re-use the same socket, lazily called: "SO_REUSEADDR". Set it using "setsockopt()".

gx
I'm not sure, but I think if turn off the SO_LINGER option via setsockopt(), it may help with that.
Ferruccio
A: 

At a basic level, sockets are either open or closed (we'll ignore the niceties of the TCP/IP state diagram here).

If your socket is closed, then nothing can send data to it. If it's open, then incoming data will be accepted and acknowledged by the TCP/IP stack until it's buffering algorithm cries "enough!". At that point, further data will not be acknowledged.

You have two choices that I can see. Either close() the socket when you want to "unlisten", and reopen it later - Use setsockopt() with the SO_REUSEADDR flag to allow you to rebind to the well-known port before TIME_WAIT2 expires.

The other choice is to keep the socket open but simply not accept() from it while you're 'busy'. Assuming you have an application-level acknowledge to requests, You load balancer would realise it's not getting a response and act accordingly.

Roddy
+1  A: 

Based on your edited version of the question, I'm not sure you have to "unlisten" or close(). Two options come to mind:

1) After you invoke listen(), connections are not actually accepted until (logically enough) you call accept(). You can "unlisten" by simply ignoring socket activity and deferring any accept()'s until you are ready for them. Any inbound connection attempts backlog onto the queue that was created when the port was opened in listen mode. Once the backlog queue is full in the stack, further connection attempts are simply dropped on the floor. When you resume with accepts(), you'll quickly dequeue the backlog and be ready for more connections.

2) If you really want the port to appear completely closed temporarily, you might dynamically apply the kernel level packet filter to the port to prevent the inbound connection attempts from reaching the network stack. For example, you could use Berkeley Packet Filter (BPF) on most *nix platforms. That is you want to drop inbound packets coming in to the port of interest using the platform's firewall features. This, of course, varies by platform, but is a possible approach.

Tall Jeff
The problem with keeping those connections in the accept() backlog is even though you haven't accept()ed them the upstream LB has no way to know your currently queueing (not processing requests)One idea might be to set the backlog to 1, so once there is a request queued, subsequent ones will fail
Dave Cheney
(why are comments limited to 300 chars?) to continue, that puts the responsibility back on my server to clear out the accept() backlog regularly, but that is what would happen anyway under ideal conditions, and under less than ideal conditions, the LB getting E_CONNREFUSED will avoid queueing cons
Dave Cheney
A: 

Here's a rather ugly approach based on your edited question:

Open a socket for listening with a normal backlog. Proceed.

When you want to "shut down", open a 2nd one with a backlog of 1 and SO_REUSEADDR. Close the first one. When ready to resume, do another socket juggle to one with a normal backlog.

Picky details around draining the accept queue from the socket that you're closing will be the killer here. Probably enough of a killer to make this approach nonviable.

Darron
+1  A: 

Some socket libraries allow you to specifically reject incoming connections. For example: GNU's CommonC++: TCPsocket Class has a reject method.

BSD Sockets doesn't have this functionality. You can accept the connection and then immediately close it, while leaving the socket open:

while (running) {

  int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
  while (noConnectionsPlease) {
    shutdown(i32ConnectFD, 2);
    close(i32ConnectFD);
    break;
  }

}
Andrew Johnson
that wont give the same effect at the protocol level as not listening or refusing a connection due to the listen backlog being full so it might not achieve what Dave wants with regard to load balancers, etc. This will just look like a short lived successful connection.
Len Holgate
It isn't perfect, but, as far as I know, with BSD sockets there is no way to unlisten without losing the socket, and no way to send a close without first getting that socket handle for the connection with an accept. If this doesn't work, then Dave will need a new socket library.
Andrew Johnson
A: 

I don't necessarily think this is a good idea, but...

You might be able to call listen a second time. The POSIX spec doesn't say not to. Perhaps you could call it a second time with a backlog parameter of 0 when you want to "unlisten".

What happens when listen is called with a backlog of 0 seems to be implementation defined. The POSIX spec says it may allow connections to be accepted, which implies some implementations may choose to reject all connections if the backlog parameter is 0. More likely though, your implementation will choose some positive value when you pass in 0 (probably either 1 or SOMAXCONN).

Dan
+1  A: 

I don't think it's a good way to signal an upstream load-balancer. It would have to actually send some connections to your server before the message got through - those connections would probably get rejected.

Likewise, any connections which were pending when you closed the listening socket will get closed with no data.

If you want to signal the upstream load balancer, you should have a protocol for doing that. Don't try to abuse TCP to do it.

Fortunately if the clients are normal web browsers, you can get away with an awful lot - simply closing sockets generally results in them retrying transparently to the user (to a point).

MarkR
A: 

The question didn't say what kind of socket. If it is a unix socket, you can stop and start listening with rename(2). You can also permanently stop listening with unlink(2), and since the socket remains open you can continue to service your backlog. This approach seems quite handy, though I have not seen used before and am just exploring it myself.

Andrew