On Tue, 2017-02-21 at 09:55 -0500, Chuck Rolke wrote: > > ----- Original Message ----- > > From: "Alan Conway" <[email protected]> > > To: "dev" <[email protected]> > > Sent: Monday, February 20, 2017 4:23:50 PM > > Subject: Picking temporary ports for tests > > > > Our code bases have a motley collection of attempts to pick safe > > ports > > for short-lived tests, most of which have race conditions that can > > result in sporadic bogus "address in use" test failures. > > > > Here's an approach that seems to work: Bind a socket with bind(0) > > and > > SO_REUSEADDR - guaranteed by the OS to get you a port. Now leave > > that > > socket open while you start your server on that port. Our servers > > also > > set SO_REUSEADDR for other reasons, this means the server can bind > > to > > the same port even though we have a socket bound already. The test > > code > > only binds, it doesn't listen, so the server is can listen as > > usual. > > Now you can close the test socket and you're off to the races! > > > > Am I only dreaming? Here's the code (proton/proton- > > c/src/test_tools.h) > > > > /* Create a socket and bind(LOOPBACK:0) to get a free port. > > Use SO_REUSEADDR so other processes can bind and listen on this > > port. > > Close the returned fd when the other process is listening. > > Fail on error. > > */ > > static sock_t sock_bind0(void) { > > int sock = socket(AF_INET, SOCK_STREAM, 0); > > TEST_ASSERT_ERRNO(sock >= 0, errno); > > int on = 1; > > TEST_ASSERT_ERRNO(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, > > (const > > char*)&on, sizeof(on)) == 0, errno); > > struct sockaddr_in addr = {0}; > > addr.sin_family = AF_INET; /* set the type of connection to > > TCP/IP */ > > addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); > > addr.sin_port = 0; /* bind to port 0 */ > > TEST_ASSERT_ERRNO(bind(sock, (struct sockaddr*)&addr, > > sizeof(addr)) == 0, > > errno); > > return sock; > > } > > > > /* Get the real port number allocated to a socket created with > > sock_bind0 */ > > static int sock_port(sock_t sock) { > > struct sockaddr addr = {0}; > > socklen_t len = sizeof(addr); > > TEST_ASSERT_ERRNO(getsockname(sock, &addr, &len) == 0, errno); > > int port = -1; > > switch (addr.sa_family) { > > case AF_INET: port = ((struct sockaddr_in*)&addr)->sin_port; > > break; > > case AF_INET6: port = ((struct sockaddr_in6*)&addr)->sin6_port; > > break; > > default: TEST_ASSERTF(false, "unknown protocol type %d\n", > > addr.sa_family); break; > > } > > return ntohs(port); > > } > > > > ----------------------------------------------------------------- > > ---- > > To unsubscribe, e-mail: [email protected] > > For additional commands, e-mail: [email protected] > > > > > > Looks like you already have this code in the (proton) code base. > * Does it work? > * Are there issues with using INADDR_LOOPBACK vs INADDR_ANY? > * It looks like it opens a port on one sa_family only (IPv4 or IPv6). > I remember one of the problems with temp ports as the port being > available on IPv4 but not on IPv6. Getting them both open was > necessary. This may not be an issue for your use case.
It's working for some new tests I'm writing, it probably needs to be fleshed out for more general use - your points are valid. I'll add some IPv6 tests (I should have that anyway) and see how it breaks... Cheers, Alan. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
