Ich versuche, eine Reihe von Anwendungen zu erkennen, die UDP verwenden und Nachrichten senden. Die Anwendungen senden regelmäßig ein UDP-Paket aus, in dem sie angeben, wer sie sind und was sie tun können. Zunächst verwenden wir nur zum Senden an INADDR_BROADCAST.Empfang von UDP-Paketen, die an 127.0.0.1 gesendet werden, wenn SO_REUSEADDR verwendet wird
Alle Anwendungen haben den gleichen zu empfangenden Port (daher SO_REUSEADDR). Ein Ereignis-Kernel-Objekt wird an den Socket angehängt, so dass wir benachrichtigt werden, wenn wir ein neues Paket holen und dieses in einer WaitFor-Schleife verwenden können. Der Socket wird asynchron verwendet.
Öffnen der Steckdose:
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);
Daten senden, um eine angegebene Liste von Adressen:
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;
Empfangen von Paketen:
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;
Wenn wir die Ausstrahlungsdaten aussenden mit INADDR_BROADCAST, die lokale Broadcast-Adresse (192.168.1.255) oder die lokale IP-Adresse funktioniert einwandfrei. In dem Moment, in dem wir 127.0.0.1 zum "Senden" verwenden, ist der Empfang sporadisch, funktioniert aber im Allgemeinen nicht.
Hat jemand eine Idee, wie man das löst (die Adressliste ist änderbar)? Wenn alles andere fehlschlägt, suche ich alle lokalen IP-Adressen und ersetze einfach 127.0.0.1, aber das lässt Probleme übrig, wenn IP-Adressen sich ändern.
Update: Wenn Sie App1 zum ersten Mal starten, empfängt App1 Pakete. Als nächstes starten Sie App2. Jetzt wird App1 weiterhin Pakete empfangen, App2 jedoch nicht. Wenn Sie App1 beenden, wird App2 Pakete empfangen. Wenn Sie App3 starten, empfängt App2 seine Pakete, App3 jedoch nicht.
Also: nur eine Anwendung wird die Pakete empfangen, wenn 127.0.0.1 verwendet wird.
Auch das Setzen von IPPROTO_IP, IP_MULTICAST_LOOP auf eins mit setsocketopt ändert nichts.
Ich werde Multicast statt Broadcast betrachten. Was ich an Ihrem Beispiel sehe, ist, dass ich in IP_ADD_MEMBERSHIP/IP_MULTICAST_LOOP schauen sollte. Danke für das Beispiel. –
Nach dem Ausprobieren funktionierte es, Multicasting statt Broadcasting zu verwenden. –