I am trying to make a set of applications discover each other using UDP and broadcasting messages. The applications will periodically send out an UDP packet saying who they are and what they can do. Initially we only use to broadcast to INADDR_BROADCAST.
All applications share the same port to listen to (hence the SO_REUSEADDR). An event kernel object is attached to the socket so we get notified when we can fetch a new packet and use that in a WaitFor loop. The socket is used async.
Opening the socket:
FBroadcastSocket := socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if FBroadcastSocket = INVALID_SOCKET then Exit;
i := 1;
setsockopt( FBroadcastSocket, SOL_SOCKET, SO_REUSEADDR, Pointer( @i ), sizeof( i ) );
i := 1;
setsockopt( FBroadcastSocket, SOL_SOCKET, SO_BROADCAST, Pointer( @i ), sizeof( i ) );
System.FillChar( A, sizeof( A ), 0 );
A.sin_family := AF_INET;
A.sin_port := htons( FBroadcastPort );
A.sin_addr.S_addr := INADDR_ANY;
if bind( FBroadcastSocket, A, sizeof( A ) ) = SOCKET_ERROR then begin
CloseBroadcastSocket();
Exit;
end;
WSAEventSelect( FBroadcastSocket, FBroadcastEvent, FD_READ );
Sending out data to a specified list of addresses:
for i := 0 to High( FBroadcastAddr ) do begin
if sendto( FBroadcastSocket, FBroadcastData[ 0 ], Length( FBroadcastData ), 0, FBroadcastAddr[ i ], sizeof( FBroadcastAddr[ i ] ) ) < 0 then begin
TLogging.Error( C_S505, [ GetWSAError() ] );
end;
end;
Recieving packets:
procedure TSocketHandler.DoRecieveBroadcast();
var
RemoteAddr: TSockAddrIn;
i, N: Integer;
NetworkEvents: WSANETWORKEVENTS;
Buffer: TByteDynArray;
begin
// Sanity check.
FillChar( NetworkEvents, sizeof( NetworkEvents ), 0 );
WSAEnumNetworkEvents( FBroadcastSocket, 0, @NetworkEvents );
if NetworkEvents.ErrorCode[ FD_READ_BIT ] <> 0 then Exit;
// Recieve the broadcast buffer
i := sizeof( RemoteAddr );
SetLength( Buffer, MaxUDPBufferSize );
N := recvfrom( FBroadcastSocket, Buffer[ 0 ], Length( Buffer ), 0, RemoteAddr, i );
if N <= 0 then begin
N := WSAGetLastError();
if N = WSAEWOULDBLOCK then Exit;
if N = WSAEINTR then Exit;
TLogging.Error( C_S504, [ GetWSAError() ] );
Exit;
end;
DoProcessBroadcastBuffer( Buffer, N, inet_ntoa( RemoteAddr.sin_addr ) );
end;
When we send out the broadcast data using INADDR_BROADCAST, the local broadcast address (192.168.1.255) or the local IP address everything works fine. The moment we use 127.0.0.1 to "broadcast" to, reception is sporadic but generally does not work.
Does anyone have a clue how to solve this (the address list is changeable)? If all else fails I will lookup all the local IP addresses and just replace 127.0.0.1 with that but that leaves problems when IP addresses change.
Update: When you first start App1, App1 will receive packets. Next you start App2. Now App1 will still receive packets but App2 won't. If you stop App1, App2 will start to receive packets. If you start App3, App2 will receive it's packets but App3 won't.
Thus: only one application will receive the packets when 127.0.0.1 is used.
Also setting IPPROTO_IP, IP_MULTICAST_LOOP to one with setsocketopt does not change anything.