8

I made a socket (Winsock2) in Visual Studio Pro C++ to listen on a port for connections (TCP). It works perfectly, but I let it run in its own thread, and I want to be able to shut it down with the hopes of restarting it later. I can terminate the thread without problems, but doing so does not stop the socket from accepting new clients (that is, it lingers on the accepts it was doing before I closed the thread). I can connect new clients to it but nothing happens... it just accepts and that's it. What I want is to stop it from listening and accepting and then be able to tell it to start up again later on the same port. Attempting to restart it now just tells me the port is already taken.

Here is the listen thread function:

DWORD WINAPI ListeningThread(void* parameter){
TCPServer *server = (TCPServer*)parameter;

try{
    server = new TCPServer(listen_port);
}catch(char* err){
    cout<<"ERROR: "<<err<<endl;
    return -1;
}

int result = server->start_listening();
if(result < 0){
    cout<<"ERROR: WSA Err # "<<WSAGetLastError()<<endl;
    return result;
}
cout<<"LISTENING: "<<result<<endl<<endl;
while(true){
    TCPClientProtocol *cl= new TCPClientProtocol(server->waitAndAccept());
    HANDLE clientThread = CreateThread(0, 0, AcceptThread, cl, 0, 0);
    cout<<"Connection spawned."<<endl;
}

return 0;
}

Here are the relevant functions in TCPServer:

TCPServer::TCPServer(int port){
listening = false;
is_bound = false;

//setup WSA
int result = WSAStartup(MAKEWORD(2, 2), (LPWSADATA) &wsaData);
if(result < 0){
    throw "WSAStartup ERROR.";
    return;
}

//create the socket
result = (serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
if(result < 0){
    throw "Socket Connect ERROR.";
    return;
}

//bind socket to address/port
SOCKADDR_IN sin;
sin.sin_family = PF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY;

result = bind(serverSocket, (LPSOCKADDR) &sin, sizeof(sin));
if(result < 0){
    throw "Could not Bind socket - Make sure your selected PORT is available.";
    return;
}

is_bound = true;
}

int TCPServer::start_listening(){
int result = -1;
if(is_bound){
    //SOMAXCONN parameter (max) is a backlog:
    //  how many connections can be queued at any time.
    result = listen(serverSocket, SOMAXCONN);
    if(result >= 0)
        listening = true;
}
return result;
}

SOCKET TCPServer::waitAndAccept(){
if(listening)
    return accept(serverSocket, NULL, NULL);
else
    return NULL;
}

I have tried both closesocket() and shutdown() but both of those threw errors.

Thank you all for your time and help!

Sefu
  • 2,404
  • 8
  • 42
  • 59
  • Slosesocket(serverSocket) ought to be the solution. How and when did you call it, and which error was produced? shutdown() is irrelevant here; it is for sending EOF on a connected TCP stream but keeping the opposite direction open. – hmakholm left over Monica Aug 07 '11 at 02:48

1 Answers1

10

First, make sure you set SO_REUSEADDR option on the server socket to be able to re-start listening.

Then, I'm guessing, your problem is that accept() blocks and you cant stop it when you need to, so you are killing the thread. Bad solution. The right answer here is asynchronous I/O, i.e. select() or poll() or their Windows counterparts. Take a look at Advanced Winsock Samples.

A quick-and-dirty solution in a multithreaded app is to check some is_it_time_to_stop_accepting_connections flag before each accept(), then when it's time to stop, flip the flag and connect to the listening port (yes, in the same program). That will unblock the accept() and allow you to do proper closesocket() or whatever.

But seriously, read up on asynchronous I/O.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • 1
    I agree that he's probably blocked on the accept call and doing an evil TerminateThread call. But after he fixes that, does he really need to use SO_REUSEADDR? Wouldn't that potentially hide any other bugs he might have with regards to not properly closing his listening socket? Let's say he forgets to stop his currently running server process before starting another one... I'd assume you'd rather error out (because another process owns the port!) than try to have two processes listening on the same port with side-effects that you don't expect. – selbie Aug 07 '11 at 04:23
  • 1
    Not closing the socket will prevent other socket from being bound to the same port and address. `SO_REUSEADDR` has a bit of a different meaning than you think. And yes, 99.9999% of all socket servers need to set this option. http://stackoverflow.com/questions/6960219/ – Nikolai Fetissov Aug 07 '11 at 04:29
  • Ahh.... So if I didn't specify SO_REUSEADDR, then the bind call would fail if an active connection spun off the previous listen socket was still around. Correct? – selbie Aug 07 '11 at 04:38
  • 2
    Yes. And even if the listening socket is closed, but there's a connected socket derived from it (accepted), it'll have the server port as source, and you won't be able to bind a new listening socket. Not setting `SO_REUSEADDR` also prevents binding port again for about 2 minutes (google TCP MSL) – Nikolai Fetissov Aug 07 '11 at 04:42