Laden...

"An existing connection was forcibly closed by the remote host" beim 4. Senden von Daten.

Erstellt von dRei vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.081 Views
D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren
"An existing connection was forcibly closed by the remote host" beim 4. Senden von Daten.

Hallo zusammen,

ich habe gerade ein kleines Problem. Ich möchte ein Protokoll implementieren, was auch eigentlich super funktioniert bis jetzt. Als Transportwege sind zwei Channels erforderlich, die mit einer bestimmten Steuerung verbunden werden. Channel A sendet daten Channel B soll Asynchrone Daten empfangen. Die versendeten Daten sind XML mit einem bestimmten Header vorangestellt. Das Projekt ist ein .NETCF35 Projekt also etwas eingeschränkt.
Folgender Ablauf:
Message 1 wird über ChannelA gesendet (eröffnung der Connection; von der Steuerung bekomme ich auf ChannelA auch prompt ein Acknowledge ohne Fehler zurück also alles in Ordnung)
Message 2 wird über ChannelA gesendet (Heartbeat-Signal nach 7 Sekunden; von der Steuerung bekomme ich auf ChannelA auch prompt ein Acknowledge ohne Fehler zurück also alles in Ordnung)
Message 3 wird über ChannelA gesendet (Heartbeat-Signal nach 7 Sekunden; Mitteilung wird nicht raus gesendet! Warum auch immer Message 3 ist baugleich mit Message 2 also kein Syntaxfehler im Protokoll!)
Message 4 soll über ChannelA gesendet werden (Heartbeat-Signal nach 7 Sekunden; Sofort beim versuch zu Senden bekomme ich schon die Exception "An existing connection was forcibly closed by the remote host"; Stelle ich nun die Verbindung wieder her, bekomme ich logischerweise einen MessageID-Zählungsfehler von der Steuerung gemeldet.)

Es scheint so, als ob einer der Threads irgendwo beendet wird. Leider habe ich im Moment scheinbar Tomaten auf den Augen und finde keinen Fehler...

Hier der Quellcode für den ChannelA. Vielleicht fällt euch ja was auf. 😕


    public class TCPConnectionA
    {
        public delegate void evh_DataReceived(string rawdata);
        public event evh_DataReceived DataReceived;

        public System.Net.Sockets.TcpClient TCPClient { get; private set; }
        
        private Controller.MainCtrl Main;
        System.Threading.Thread listenerthread;
        private System.Net.Sockets.NetworkStream clientStream;

        public TCPConnectionA(Controller.MainCtrl mainCtrl)
        {
            Main = mainCtrl;
            TCPClient = new System.Net.Sockets.TcpClient();
            TCPClient.Client.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(Main.SettingsCtrl.GetSetting(Globals.API.Helper.RegistryValueNames.SettingName_PROTOKOLL_NetworkInterface)), 0));
            TCPClient.Connect(System.Net.IPAddress.Parse(Main.SettingsCtrl.GetSetting(Globals.API.Helper.RegistryValueNames.SettingName_PROTOKOLL_ChannelA_IP)),
                Main.SettingsCtrl.GetSettingInt(Globals.API.Helper.RegistryValueNames.SettingName_PROTOKOLL_ChannelA_Port).Value);
            clientStream = TCPClient.GetStream();
            listenerthread = new System.Threading.Thread(new System.Threading.ThreadStart(StartReading));
            listenerthread.Name = "ListenerThread_ChannelA";
            listenerthread.Start();
        }


        public void SendData(string rawData)
        {
            ASCIIEncoding encoder = new ASCIIEncoding();
            while (!clientStream.CanWrite)
                System.Threading.Thread.Sleep(10);
            clientStream.Flush();
            clientStream.Write(encoder.GetBytes(rawData), 0, rawData.Length);
            Debug.WriteLine(rawData);
        }

        private void StartReading()
        {
            byte[] message = new byte[4096];
            int bytesRead;

            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead != 0)
                {
                    //message has successfully been received
                    ASCIIEncoding encoder = new ASCIIEncoding();
                    string s = encoder.GetString(message, 0, bytesRead);
                    if (DataReceived != null)
                        DataReceived(s);
                }
            }
        }
    }

EDIT#1: Protokollnamen entfernt
EDIT#2: Verbesserungsvorschlag von weismat

4.221 Beiträge seit 2005
vor 11 Jahren

Ich hätte jetzt mal spontan gesagt, dass die Steuerung zwischen Message 2 und Message 3 abraucht.

Message 3 läuft dann in Nirwana... und Message 4 löst dann bei Dir den Fehler aus.

Somit ist nicht die Frage wo und ob bei Dir ein Thread abraucht... sondern wieso bricht die Steuerung die Verbindung ab.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren

Hallo Programmierhans,

die Idee hatte ich auch schon. Überprüft habe ich das dan aber mit einer funktionierenden Testanwendung was übertragen wird. Die Mitteilungen sind absolut identisch und die Testanwendung bekommt auch ein Acknowledge auf Message 3. Mit Wireshark habe ich mich abgesichert, dass die Testanwendung nicht doch noch andere Daten sendet. Der entstehende Traffic ist 1:1 der selbe. In der Testanwendung funktioniert es und in meiner Anwendung nicht, was mir halt gerade echt den letzten Nerv raubt, weil ich nicht verstehe wieso die Steuerung auf EXAKT die selben Messages zwei mal unterschiedlich reagieren sollte 😕

W
872 Beiträge seit 2005
vor 11 Jahren

Sowas hier wuerde ich nicht bei jedem Senden neu machen:

 System.Net.Sockets.NetworkStream clientStream = TCPClient.GetStream();

Mach daraus doch eine privates Attribute, dass Du im Konstruktor initialisierst.

D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren

Hallo Weismat,

danke für den Hinweis. Das ist angepasst, ändert allerdings leider nichts am fehlverhalten der Anwendung. 😕

4.221 Beiträge seit 2005
vor 11 Jahren

Mach mal noch ein try/catch um "DataReceived"

Wenn du da in der EventBehandlung in einen unbehandelten Fehelr läufst, dann kackt Dir der "ListenerThread_ChannelA" ab.

Wenn dies beim Ack von Mess2 passiert, dann nimmt niemand mehr die Daten aus dem Ack von Mess3 ab...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

U
1.688 Beiträge seit 2007
vor 11 Jahren

Hallo,

wie sehen denn die versendeten Daten aus (auch im Vergleich zur funktionierenden Testanwendung)? Vielleicht treten ja auch schon bei den ersten beiden Nachrichten Fehler auf, die sich erst bei der dritten bemerkbar machen.
Anscheinend beendet ja die Steuerung die Verbindung. Dann wird bytesRead 0 sein, eine SocketException bekommst Du erst mal nicht. Das Verhalten des Threads kannst Du durch Logging überprüfen.

D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren

Hallo Programmierhans,

mensch jetzt hatte ich echt gedacht das könnte es sein 😃 War mir garnicht mehr aufgefallen, dass das passieren könnte. Leider war es das auch nicht. In der Event-Behandlung mache ich auch bisher nur ein Debug.WriteLine mit den Daten 😕. Hätte mich schon gewundert, wenn das in eine Exception gelaufen wäre, aber nun ist es abgesichert und ich kann später auch sicher sein, dass es dort nicht knallt.

Hallo ujr,

Naja wirklich effektiv kann ich das leider nicht Posten.
Laut Protokolldefinition müssen die Namespaces nicht immer mit übertragen werden um Traffic einzusparen. Dies muss nur geschehen, wenn an der Steuerung ein Parameter gesetzt wird (je session nicht dauerhaft).
OpenConnectionMessage notizen:

  1. Testanwendung node 5 ist kein Zwang und brauche ich auch noch nicht in meiner Anwendung!
  2. Testanwendung node 6 entspricht meiner node 5.
    Ich werde mal die versendeten Nachrichten abändern und hier anhängen:

Message 1 aus der Testanwendung:

55AA0001000007560001000000000000<?xml version="1.0" encoding="UTF-8"?>
<root:ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:root="LOCATION" xmlns:common="LOCATION" xmlns:msl_msg="LOCATION" xsi:schemaLocation="LOCATION">
  <node1>
    <node2>20</node2>
    <node3>0</node3>
    <node4>
      <node4.1>2012-06-14</node4.1>
      <node4.2>17:58:59</node4.2>
    </node4>
    <node5>
      <node5.1>0</node5.1>
      <node5.2>1</node5.2>
      <node5.3>0</node5.3>
      <node5.4>0</node5.4>
      <node5.5>0</node5.5>
      <node5.6>0</node5.6>
    </node5>
    <node6>
      <node6.1>5</node6.1>
      <node6.2>5</node6.2>
    </node6>
  </node1>
</root:ROOT>

Meine Message:

55AA0001000001560001000000000000
<?xml version="1.0" encoding="UTF-8"?>
<root:ROOT>
  <node1>
    <node2>20</node2>
    <node3>0</node3>
    <node4>
      <node4.1>2012-06-14</node4.1>
      <node4.2>18:16:20</node4.2>
    </node4>
    <node5>
      <node5.1>5</node5.1>
      <node5.2>5</node5.2>
    </node5>
  </node1>
</root:ROOT>

Heartbeat aus der Testanwendung:

55AA0001000007560001000000000000<?xml version="1.0" encoding="UTF-8"?>
<root:ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:root="LOCATION" xmlns:common="LOCATION" xmlns:msl_msg="LOCATION" xsi:schemaLocation="LOCATION">
  <node1>
    <node2>20</node2>
  </node1>
</root:ROOT>

Mein Heartbeat:

55AA0001000001560001000000000000
<?xml version="1.0" encoding="UTF-8"?>
<root:ROOT>
  <node1>
    <node2>20</node2>
  </node1>
</root:ROOT>

EDIT#1: Namespaces

U
1.688 Beiträge seit 2007
vor 11 Jahren

Dass die Zeichen vor <?xml... nicht übereinstimmen ist Absicht? Auch der zusätzliche Zeilenumbruch?

D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren

Eher unwahrscheinlich. Ich sende die Daten komplett ohne CRLF.
Um das auszuschließen werde ich bei mir noch die CRLF einfügen, wie sie auch bei der Testanwendung sind.

Um ganz sicher zu gehen habe ich nun auch in meinen Messages die Namespaces mit aufgenommen und die Node5 der Testanwendung ebenfalls so implementiert.
Leider verabschiedet sich der TCPClient immer noch scheinbar irgendwo zwischen Message 2 und 3.

U
1.688 Beiträge seit 2007
vor 11 Jahren

Um das auszuschließen werde ich bei mir noch die CRLF einfügen, wie sie auch bei der Testanwendung sind.

Weiter oben ist bei Dir aber gerade ein CRLF, während in der Testanwendung keines ist.

Aber was ist denn nun mit den Unterschieden in den Ziffern (7 <-> 1)?

4.221 Beiträge seit 2005
vor 11 Jahren

Im XML hast DU UTF8

Müsstest Du da nicht Encoding.UTF8 statt ASCII nehmen ?

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

D
dRei Themenstarter:in
23 Beiträge seit 2009
vor 11 Jahren

Programierhans:
Danke für den Hinweis. Encoding ist umgebaut auf UTF-8

ujr:
Alle CRLF hatte ich wegen der Lesbarkeit eingepflegt. Ich hatte bei mir garkeine drin.
Auch nach der Anpassung ist das Verhalten nach wie vor gleich.
Die unterschiede in den Ziffern 7 und 1
Header:
Byte 00-03 HeaderKennung
Byte 04-07 Zählernummer
Byte 08-15 Größe (ohne Header)
Byte 16-19 Typ
Byte 20-23 Fehlercode
Byte 24-32 Reserviert

EDIT#1: Es scheint doch irgendwas mit der Steuerung nicht zu stimmen. Nachdem ich den Intervall für den Heartbeat auf 3 Sekunden reduziert habe, wurde bis Message 5 alles einwandfrei übertragen. Danach war aber wieder sense.
Ich werde mal versuchen den Hersteller zu erreichen und zu fragen wo dort das Problem liegen könnte. Kommt mir zwar immernoch etwas komisch vor das ganze aber es ist auf jeden Fall mal ein anderer Ansatz.

EDIT#2: Entschuldigt bitte. Ich hatte mich so sehr auf dieses Problem festgefahren, dass ich nur noch einen Tunnelblick hatte. Wie im ersten Post beschrieben, gibt es zwei connections. Channel B ist hingegangen und hat für jeden Client einen eigenen Thread erstellt. - Leider wurde dieser aber nicht gestartet, was dazu geführt hat, dass die Acknowledge welche ich auf Channel2 senden muss nicht gesendet wurden und die Steuerung hat nach dem Timeout die Verbindung zu gemacht. Sinnvollerweise teilte mir auch dies die Steuerung auf dem nicht gestartetem Channel B-Clientthread mit 😕

Entschuldigt bitte, aber manchmal hat man wirklich ein Brett vorm Kopf 🤔 X(