Doug Binks - 05 Dec 2013
I was recently testing my LAN server browser dialogue menu with multiple machines running servers when I encountered an unusual issue. I had two machines, each running a server and a client. On one machine the client could see both servers, but on the other machine only the local server was found. The application had firewall permissions, and since the server was both sending and receiving packets without problem the firewall didn't seem to be the culprit.
Working LAN discovery with two servers running on two different machines. Note the two IP addresses listed for Machine 1.
I've been writing TCP/IP networking code (specifically UDP for gaming) on and off for two decades and this was the first time I'd had issues on what appeared to be a relatively simple network topology. Things can get tough when you're in a company with routers and a large internal address space, but with all machines on one router this should have worked. So I was a bit surprised.
LAN network browsing is important for the case where players can't connect to the internet to communicate with a master server browser but do have a local connection, or simply want to run their own private game quickly.
The penny dropped when I noticed that the client on the problem machine was finding the local server on a different address to that the other machines were showing. This wasn't the loop-back (127.0.0.1) address, but another IP in an internal only range. Checking my network adapters I noticed I had an ethernet adapter which had been installed by VirtualBox. Although I didn't have any machines running, the client was somehow performing network discovery via that adapter. It appears that the Windows TCP/IP stack doesn't propagate the 255.255.255.255 broadcast to all adapters, so the broadcast ping was only local.
The solution was to enumerate all of the host IP addresses, and in addition to broadcasting a ping to 255.255.255.255 I also send a ping to each address with the last byte replaced with 255. With most small networks having a subnet mask of 255.255.255.0 this should work well. This will result in multiple servers being found if they're on a the same machine as the client and there is more than one ethernet adapter, but is unlikely except during development and also doesn't pose a problem to finding a server.
A final solution would be to ping every IP address on your subnet in case of an unusual subnet mask, but a better approach might be to have servers display their IP and clients able to input it manually as this should cover all bases.
To find out what network addresses your system has, you need to call gethostname to get the name of the current machine, and then call gethostbyname (note the 'by' here) to go from the host to a structure containing a list of address or you can use the getaddrinfo function. The socket will need to have broadcast permissions set with setsockopt. I've used links to the Windows documentation here as that's the OS I discovered the issue on. The issue may also exist on other systems but I've not as yet tested this. The solution shouldn't cause any problems, so I'm using it across all platforms.
If like me you're using Raknet, the code would look something like this:
//ping the server const unsigned int NETWORK_PORT_SERVER = 23947; pRakPeerInterface->Ping( "255.255.255.255", NETWORK_PORT_SERVER, true ); //also ping each IP address at X.Y.Z.255 in case of multiple adapters unsigned int numLocalAddresses = pRakPeerInterface->GetNumberOfAddresses( ); for( unsigned int address = 0; address < numLocalAddresses; ++address ) { std::string strAddress = pRakPeerInterface->GetLocalIP( address ); size_t idx = strAddress.find_last_of( '.' ); if( idx < strAddress.length() ) { std::string pingAddress = strAddress.substr( 0, idx ) + ".255"; pRakPeerInterface->Ping( pingAddress.c_str(), NETWORK_PORT_SERVER, true ); } }
To confirm this wasn't Raknet related I've replicated the issue with my own (less sophisticated) networking layer from the original Avoyd, and the solution works there too.
If you've got any questions on the implementation details, or you'd simply like to know more about networking let me know in the comments below. Meanwhile happy LAN discovery, free from multiple adapter confusion!