7
votes

My task is to implement a two player game played between two computers connected via TCP. One of the requirement is that only the winner is given the choice play again or not. In case the server wins and decides not to play further, the client should restart as a server and accept new connections.

My approach: If game LOST (in client mode), close sockfd and recreate another one. Then use setsockopt to allow rebinding using SO_REUSEADDR, then calling bind.

int yes = 1;
if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 )
{
    perror("setsockopt");
}

if ( bind(sockfd, (struct sockaddr*)&svr, sizeof(svr) ) == -1 )
{
    perror("server: bind");
}

But still, I'm getting the same "Address already in use" error. I've tried sleeping for 150 seconds before recreating the socket and this method works.

NOTE: I'm testing this on the same PC. It may work on two linked PCs but its a necessity to make it work on the same PC. Please help.

3
Please show your code (particularly the part around setsockopt()) so we can help.Greg Hewgill
Are you sure you're properly closing the server-side (listening socket) before attempting to recreate it, in the client code? On a Linux system, "netstat -tlp" should identify what's holding a TCP port open to listen on it...BRPocock
You attempt to bind() the same {address,portnum} twice on one machine? What would you expect to happen?wildplasser
Well, after waiting for 120 secs, the approach works. So I guess its okay to expect it to work immediately by using SO_REUSEADDR. However, please tell me if I'm wrong.tecfreak
@BRPocock: You're right. There was a sleep(1) before the socket closed, thats why I was unable to bind. Now, it works perfectly. Thanks for the hint! :)tecfreak

3 Answers

3
votes

SO_REUSEADDR only allows you to simultaneously bind to a more a specific address, i.e. first server listens on INADDR_ANY (all interfaces) and subsequent servers listen on different specific interface addresses.

Second scenario is when listening TCP socket accepts a connection, which is kept in use, but the listening socket itself is closed and then re-opened - say parent server process exits and starts again.

In both cases you need to always set SO_REUSEADDR option on the listening socket before you call bind(2).

2
votes

Since you are running this on the same system, it sounds like you have a race condition. The client is trying to bind() the socket just before the server has closed it (assuming both server and client are setting SO_REUSEADDR on their sockets).

You need to implement some kind of handshake that allows the server to inform the client after it has closed the listening socket - perhaps the server should close the listening socket before it closes the active socket from the last game?

0
votes

Setting this socket option allow local address reuse. If a problem is encountered when attempting to bind to a port which has been closed but not released (may take up to 2 minutes as defined by TIME_WAIT). Apply the SO_REUSEADDR socket option to release the resource immediately and to get around the TIME_WAIT state. 0 = disables, 1 = enables.

Allows other sockets to bind() to this port, unless there is an active listening socket bound to the port already. This enables you to get around those "Address already in use" error messages when you try to restart your server after a crash