3

The following is the essence of my test fixture -

SetUp()
{
    g_listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    /* localhost is the server */
    bind(g_listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    listen(g_listen_sock, max_connections);
}

testcase()
{
    hdl = accept(g_listen_sock, NULL, NULL);
    -- send()/recv() data on the socket --
}

TearDown()
{
    shutdown(g_listen_sock, SHUT_RDWR);
    close(g_listen_sock);
    g_listen_sock = INVALID_SOCKET;
}

In the normal use of the application the listening socket is bound only once during the lifetime of the application, however the test setup repeatedly opens and closes the listening socket. The first iteration of the testcase works fine but subsequent iterations fail at the bind() call with errno == 98 i.e. EADDRINUSE.

How do I avoid this situation? The solution ideally wouldn't require me to have a separate test version of the code, for e.g. using SO_REUSEADDR while testing.

P.S. - the same code works fine on Windows, the bind() failure happens on Linux.

work.bin
  • 1,078
  • 7
  • 29
  • 2
    Possible duplicate of [Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ?](https://stackoverflow.com/q/14388706/608639), [TCP option SO\_LINGER (zero) - when it's required](https://stackoverflow.com/q/3757289/608639) and [Best practice for closing sockets. SO_LINGER or shutdown/close?](https://stackoverflow.com/q/22873616/608639) – jww Jul 06 '18 at 14:49
  • `however the test setup repeatedly opens and closes the listening socket.` Dont do that. Instead cease `select()`ing the listen socket, and cease `accept()`ing incoming connections. The clients will have to wait a bit (instead of getting a Connection Reset) – joop Jul 06 '18 at 15:09

1 Answers1

9

What you are trying to get around is a built-in functionality of the TCP layer of networking. The linux kernel will not allow you to rebind that socket because the closed socket will be in the TIME_WAIT state. There is nothing you can do to circumvent that besides using SO_REUSEADDR (as you have already pointed out), or by using a different port for each test which it doesn't sound like you want to do either.

Unfortunately, TCP was not designed to close and open the same IP/port multiple times in a row for testing, so you will have to pick your poison if you still want to do this kind of testing.

Also see this answer for a more in-depth exploration of your problem.

ritlew
  • 1,622
  • 11
  • 13
  • Why is it different in Windows with regards to the repeated reopening? – work.bin Jul 06 '18 at 14:44
  • Honestly, I am not sure why that is happening for you. When I do socket programming on windows, I usually run into the same problem you're having. As it stands though, Windows and Linux are different operating systems, and the problem is at the operating system level (so to speak). So it is just a result of the different implementations of TCP between the OS's. – ritlew Jul 06 '18 at 14:48
  • It's fairly trivial to change the TIME_WAIT delay in the Windows registry. Best to do it on a dedicated test box, however, since it's a machine-wide setting. – Martin James Jul 06 '18 at 17:06