Я пытаюсь заставить набор приложений обнаруживать друг друга, используя UDP и широковещательные сообщения. Приложения будут периодически отправлять пакет UDP, сообщая, кто они и что они могут сделать. Первоначально мы используем только для трансляции в INADDR_BROADCAST.
Все приложения используют один и тот же порт для прослушивания (отсюда и SO_REUSEADDR). Объект ядра событий присоединен к сокету, поэтому мы получаем уведомление, когда можем получить новый пакет и использовать его в цикле WaitFor. Разъем используется асинхронно.
Открытие розетки:
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 );
Отправка данных на указанный список адресов:
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;
Получение пакетов:
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;
Когда мы отправляем широковещательные данные, используя INADDR_BROADCAST, локальный широковещательный адрес (192.168.1.255) или локальный IP-адрес, все работает нормально. В момент, когда мы используем 127.0.0.1 для «широковещательной» передачи, прием является спорадическим, но обычно не работает.
Кто-нибудь знает, как решить эту проблему (список адресов можно изменить)? Если ничего не помогает, я буду искать все локальные IP-адреса и просто заменю 127.0.0.1 этим, но это оставляет проблемы при изменении IP-адресов.
Обновление:
При первом запуске App1 App1 будет получать пакеты.
Затем вы запускаете App2. Теперь App1 будет по-прежнему получать пакеты, а App2 - нет.
Если вы остановите App1, App2 начнет получать пакеты.
Если вы запустите App3, App2 получит свои пакеты, а App3 - нет.
Таким образом: только одно приложение получит пакеты при использовании 127.0.0.1.
Также установка IPPROTO_IP, IP_MULTICAST_LOOP в значение с параметром setsocketopt ничего не меняет.