Laden...

[erledigt] UDP Probleme über Loopback

Erstellt von gelöschtem Konto vor 12 Jahren Letzter Beitrag vor 12 Jahren 9.488 Views
Gelöschter Account
vor 12 Jahren
[erledigt] UDP Probleme über Loopback

Hallo,

ich bin nach langer Zeit wieder hier im Forum, da ich ein Problem auch nach langem Debuggen und Suchen nicht lösen konnte.

Ich schreibe an einer Netzwerk-Anwendung mit Client-Server Struktur, und nutze dabei UDP, da Geschwindigkeit bei der Verbindung wichtiger ist als Zuverlässigkeit. (Es soll ein kleines 2D Spiel werden)

Client und Server Empfangen beide asynchron Daten (UdpClient.BeginReceive).
Das funktioniert auch wunderbar, solange Client und Server nicht auf der selben Maschine laufen. (Habs im LAN und übers Internet getestet)
Sobald die Verbindung übers Loopback (127.0.0.1) läuft gibts ein Problem:
Wenn sich einer der beiden Programme beendet lösen die Methoden UdpClient.BeginReceive und UdpClient.EndReceive im anderen Programm eine SocketException aus.
Das ist zum einen unlogisch, weil UDP ja angeblich verbindungslos ist.
Außerdem wird dadurch irgendwie das Socket zerstört, jeder weitere Aufruf von UdpClient.BeginReceive wird sofort mit einer weiteren SocketException quittiert.
Das mag man beim Client verschmerzen können, aber beim Server brechen damit die Verbindungen zu allen weiteren Clients ab.

Gibts es dazu eine Lösung oder einen Work-Around?

MfG janismac

S
401 Beiträge seit 2008
vor 12 Jahren

Hallo,

ich vermute mal ganz stark, dass der Server und Client den gleichen Port benutzen.

Port (Protokoll)
Socket (Software)

Ein Socket besteht aus einer IP-Adresse + Port-Nummer.
Beispiel:
Der Server benutzt 127.0.0.1:1001 zum senden und empfangen.
Der Client benutzt ebenfalls 127.0.0.1:1001.

Somit benutzen beide das (der/die/das Socket ?) gleiche Socket. Dies führt zu großen Problemen. Zum senden einer Nachricht benötigt man die Adresse des Empfängers. Wenn beide die gleiche Adresse (Socket) besitzen, wer soll die Nachricht erhalten?

Wenn der Server und Client nicht auf dem selben Computer installiert sind, dann funktioniert dein vorgehen ohne Probleme, da
Server: 192.168.2.2:1001
Client: 192.168.2.3:1001
sind und zwei unterschiedliche Sockets besitzen.

Gruß, Thomas

Gelöschter Account
vor 12 Jahren

Das kann eigentlich nicht sein.

Server:

const int localPort = 4823;
// ...
UDP = new UdpClient(new IPEndPoint(IPAddress.Any, localPort));

Client:

UDP = new UdpClient();

Der Server bekommt einen festen Port zugewiesen. Der Client startet den UDP Socket ohne Parameter, bekommt also vom System einen freien Port zugewiesen.. oder?
Habs auch getestet, einmal war der Client Port 53595.
Außerdem funktioniert der Datentransfer in beide Richtungen, nur das Schließen bereitet Probleme.

Die genaue Fehlermeldung der Exception (mit MSDN ergänzt):> Fehlermeldung:

WSAECONNRESET
10054
Connection reset by peer.
An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.

Habs nach dem ich die Fehlermeldung so komplett gesehen habe im Client beim Schließen ein "UDP.Close();" eingefügt, aber das ändert auch nichts.

MfG janismac

Gelöschter Account
vor 12 Jahren

Ich habe noch mal gründlicher gesucht und gesehen, dass ich nicht der erste mit diesem Problem bin.
In 3 anderen Foren wurde das gleiche Problem beschrieben, aber nirgends eine Lösung in sicht.

Für mich sieht das aus wie ein Bug.
Vielleicht wäre es mal an der Zeit MS direkt anzuschrieben?

Hier die anderen Foreneinträge
Winsock Error 10054
Python socket error on UDP data receive. (10054)
back to basics 😦 error 10054

S
401 Beiträge seit 2008
vor 12 Jahren

Die MSDN-Seite zum Error-Code: Winsock Error Codes
UdpClient: UdpClient.Receive Method

Kannst du mal den Code für die Kommunikation Server <-> Client bereitstellen.
Wie sieht dein Client aus? Rufst du die connect-Methode auf?
Wann bekommst du die Fehlermeldung? z.B. wenn der Server gestoppt wird.

Die Fehlermeldung sagt aber eigentlich schon alles. Sie tritt auf, wenn der Endpunkt nicht mehr erreichbar ist.

Das ist zum einen unlogisch, weil UDP ja angeblich verbindungslos ist.

UDP ist verbindungslos. Aber das muss ja noch lange nicht heisen, dass der UdpClient trotzdem verbindungslos implementiert wurde.
Auszug aus der MSDN-Doku: UdpClient.Connect Methode, Remarks

The Connect method establishes a default remote host using the value specified in the endPoint parameter. Once established, you do not have to specify a remote host in each call to the Send method.

Establishing a default remote host is optional. Specifying a default remote host limits you to that host only. If you want to send datagrams to a different remote host, you must make another call to the Connect method or create another UdpClient without a default remote host. If you have established a default remote host and you also provide a remote host in your call to the Send method, Send will throw a SocketException. If you receive a SocketException, use SocketException.ErrorCode to obtain the specific error code. Once you have obtained this code, you can refer to the Windows Sockets version 2 API error code documentation in MSDN for a detailed description of the error.

If you call the Connect method, any datagrams that arrive from an address other than the specified default will be discarded. You cannot set the default remote host to a broadcast address using this method unless you inherit from UdpClient, use the Client method to obtain the underlying Socket, and set the socket option to SocketOptionName.Broadcast.

You can however, broadcast data to the default broadcast address, 255.255.255.255, if you specify IPAddress.Broadcast in your call to the Send method. If your application requires greater control over broadcast addresses, you can also revert to using the Socket class.

Wenn sich einer der beiden Programme beendet lösen die Methoden UdpClient.BeginReceive und UdpClient.EndReceive im anderen Programm eine SocketException aus.

Ah, jetzt sehe ich, dass du den Client und den Server als UdpClient implementiert hast. Ähm ja. Der Name xyzClient sollte eigentlich doch schon alles sagen. Implementiere den Server als

IPEndPoint remote_endpoint;
Socket server_udp_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
server_udp_socket.Bind(server_endpoint);

und den Client als UdpClient. Beim beenden des Servers musst du dafür sorgen, dass der Client zuvor die Verbindung zum Server beendet.

Gelöschter Account
vor 12 Jahren

Ich hab den Server auf Socket umgeschrieben wie du gesagt hast. -> kein Effekt.

Dann hab ich Server und Client beide auf die synchrone Receive() methode umgestellt (jeweils mit PollingThread) um das als Fehlerquelle auszuschließen.
Und den Client hab ich auch noch auf Socket (ohne UdpClient) umgeschrieben.
Resultat: Alles genau wie vorher.

Beim beenden des Servers musst du dafür sorgen, dass der Client zuvor die Verbindung zum Server beendet.

Das ist das Problem, wenn der Client sich beendet wird im Server eine "SocketException" ausgelöst.
Scheinbar schließe ich den Socket nicht richtig, aber habe alles ausprobiert (Disconnect, Close, etc...).

Kannst du mal den Code für die Kommunikation Server <-> Client bereitstellen.
Wie sieht dein Client aus? Rufst du die connect-Methode auf?
Wann bekommst du die Fehlermeldung? z.B. wenn der Server gestoppt wird.

Receive:

        void NetworkPolling()
        {
            while (true)
            {
                EndPoint RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                Packet p = new Packet();
                byte[] buffer = new byte[8192];


                int byteCount = UdpSocket.ReceiveFrom(buffer, ref RemoteEndPoint); // HIER kommt die SocketException mit Errorcode 10054 {"Eine vorhandene Verbindung wurde vom Remotehost geschlossen"}

                byte[] buffer2 = new byte[byteCount];
                for (int i = 0; i < byteCount; i++)
                    buffer2[i] = buffer[i];

                p.decode(buffer2);
                p.source = (IPEndPoint)RemoteEndPoint;

                lock (packetQueue)
                {
                    packetQueue.Enqueue(p);
                }
            }
        }

Send:

        void UdpSend(Byte[] sendBytes, IPEndPoint EP)
        {
            UdpSocket.SendTo(sendBytes, EP);
        }

Connect (im Client):


        private void connect(IPEndPoint EP, string name)
        {
            Packet p = new Packet();

            // ... fill packet

            // setup local endpoint to listen for packets
            UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            // send first msg to server
            UdpSend(p.encode(), EP);

            NetworkPollingThread = new Thread(new ThreadStart(NetworkPolling));
            NetworkPollingThread.Start();
        }

Socket Setup (im Server):

            ServerUdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            ServerUdpSocket.Bind(new IPEndPoint(IPAddress.Any, localPort));
Gelöschter Account
vor 12 Jahren

habe jetzt einen work-around hinbekommen
wenn ein socket "crasht" also diese fehlermeldung ausspuckt wird er beendet, und ein neuer mit gleichem port erzeugt.
aber ne lösung ist das nicht 😕

        void NetworkPolling()
        {
            while (true)
            {
                EndPoint RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                Packet p = new Packet();
                byte[] buffer = new byte[8192];

                try
                {
                    int byteCount = UdpSocket.ReceiveFrom(buffer, ref RemoteEndPoint);

                    byte[] buffer2 = new byte[byteCount];
                    for (int i = 0; i < byteCount; i++)
                        buffer2[i] = buffer[i];

                    p.decode(buffer2);
                    p.source = (IPEndPoint)RemoteEndPoint;
                    p.packetReceivedTime = DateTime.Now;

                    lock (packetQueue) packetQueue.Enqueue(p);
                }
                catch (SocketException ex)
                {
                    lock (UdpSocket)
                    {
                        int port = ((IPEndPoint)UdpSocket.LocalEndPoint).Port;
                        UdpSocket.Close();
                        UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                        UdpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
                    }
                }
            }
        }
M
8 Beiträge seit 2010
vor 12 Jahren

UDP ist ein Verbindungsloses Protokoll, wie am Anfang schon richtig erkannt - ich frage mich, wie - und warum bei dieser Diskussion ständig von "Verbindungen" gesprochen wird
Den "Server" interessiert in diesem Fall garnicht, ob der Client da ist. Es wird zu keinem Zeitpunkt eine Verbindung ethabliert.

Ich arbeite auch grade mit UDP und empfehle Dir, das ganze mal auf IP-Ebene anzuschauen. In meinem Fall "baue" ich mir die IP-Pakete incl. Header UDP und DNS Protokoll selbst zusammen und setze diese auch auf IP-Ebene ab.
Und siehe da - sie kommen sogar an! 😉

Gelöschter Account
vor 12 Jahren

Danke, aber ich denke ich kenne mich mit IP/TCP+UDP gut genug aus.
Und ich hätte auch nicht angefangen von einer Verbindung zu sprechen, wenn es nicht in der Fehlermeldung aufgetaucht wäre.

Das Problem besteht NUR wenn Client und Server auf dem gleichen Rechner laufen und über 127.0.0.1 kommunizieren.
Daher ist es naheliegend, dass betriebssystem-intern eine Kommunikation darüber statt findet, welche UDP ports "binded" sind.

Durch die Exception wird das Socket unsinnigerweise unbrauchbar (vielleicht ein "copy+paste" fehler in Windows, denn in bei TCP wäre das sinnvoll).

MfG janismac

G
538 Beiträge seit 2008
vor 12 Jahren

Das Problem ist vermutlich, dass du ein ICMP Unreachable bekommst und sich das auf deinen Host bezieht und da localhost nunmal localhost ist .... Wie das auch exakt aussehen mag - das hier sollte eine Lösung anbieten. (Die sprechen hier von "connected" und "unconnected")

How to recover gracefully from a soketexception

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

Gelöschter Account
vor 12 Jahren

(Die sprechen hier von "connected" und "unconnected")

Ist mir bewusst. Ich benutze den Socket "unconnected". Trotzdem wird das Socket durch die Exception unbrauchbar.

Aber danke für die Recherche.

MfG janismac

U
1.688 Beiträge seit 2007
vor 12 Jahren

Hallo,

Der Name xyzClient sollte eigentlich doch schon alles sagen.

Ein UdpClient kann allerdings tatsächlich als Server fungieren.

Zum Problem an sich. Leider fehlt eine nachvollziehbare Beschreibung, welcher Code zu dem Problem führt (am besten minimal lauffähig) und wie das Problem dann reproduziert werden kann.
Client und Server auf einem Rechner stellen (i. allg.) eigentlich kein Problem dar - es muss also am Code oder an der Umgebung liegen.

Gelöschter Account
vor 12 Jahren

Zum Problem an sich. Leider fehlt eine nachvollziehbare Beschreibung, welcher Code zu dem Problem führt (am besten minimal lauffähig) und wie das Problem dann reproduziert werden kann.

Der minimal-code ist angehängt.

Fehlerreproduktion:*Server starten *Client starten *(Testweise Nachrichten über die Konsolen hin und her schicken) *Eines der beiden Programme schließen (spielt keine Rolle welches) *Im verbleibenden Programm eine Nachricht schicken *-> BUMM (Screenshot von der Exception im VC# 2010 Express dabei)

MfG janismac

Hinweis von gfoidl vor 12 Jahren

Projekte - genauso wie Bilder - bitte direkt anhängen. Sollte der Anhang zu groß sein, dann schick eine PM ans Team und wir laden das dann hoch.

U
1.688 Beiträge seit 2007
vor 12 Jahren

Hallo,

mit der geposteten Schrittfolge konnte ich das Problem nachvollziehen.

Es tritt im übrigen auch dann auf, wenn nur der Client ohne den bzw. vor dem Server gestartet wird. Der Grund ist in allen Fällen gleich: es wird etwas verschickt an eine nicht erreichbare Adresse. Es kommt also das erwähnte ICMP - Destination unreachable, das den Socket aus dem blockierenden Receive holt (Available ist komischerweise 1) und die Exception auslöst.

In meinen Versuchen konnte ich allerdings die Exception fangen und ReceiveFrom erneut aufrufen und dann weiter über den Socket kommunizieren.

Alternativ kann man natürlich auch für das Empfangen einfach einen zweiten Socket öffnen, der diese Probleme überhaupt nicht mehr hat.

An sich sollte das Verhalten nicht auf Loopback-Adressen beschränkt sein. Evtl. gibt ein Switch oder Router nur die ICMP-Pakete nicht weiter, weshalb der Fehler da nicht auftritt.

Gelöschter Account
vor 12 Jahren

In meinen Versuchen konnte ich allerdings die Exception fangen und ReceiveFrom erneut aufrufen und dann weiter über den Socket kommunizieren.

Oh, das hatte ich wohl verworfen weil es mit "UdpClient" nicht funktioniert hat. Vielen Dank.

Fazit:
Um UDP zu verwenden sollte man eher "Socket" als "UdpClient" nutzen.

Problem gelöst, bitte schließen!

MfG janismac

Hinweis von gfoidl vor 12 Jahren

bitte schließen!

Geschlossen wird er nicht, aber du kannst wie in [Hinweis] Wie poste ich richtig? (im PS) beschrieben den Titel mit [er[nop][/nop]ledigt] präfixen.