Die Netzwerk-Datenrennen-Bedingung aus der Hölle
Ich habe einen Netzwerkclient / -server (Windows XP / C #) geschrieben, um mit einer ähnlichen Anwendung auf einer wirklich alten (Encore 32/77) Workstation zu arbeiten, die von einem anderen Entwickler geschrieben wurde.
Die Anwendung hat im Wesentlichen bestimmte Daten auf dem Host freigegeben / bearbeitet, um den Host-Prozess zu steuern, auf dem das System mit unserer ausgefallenen PC-basierten Touchscreen-Benutzeroberfläche mit mehreren Monitoren ausgeführt wird.
Dies geschah mit einer dreischichtigen Struktur. Der Kommunikationsprozess liest / schreibt Daten zum / vom Host, führt alle erforderlichen Formatkonvertierungen durch (Endianness, Gleitkommaformat usw.) und schreibt / liest die Werte in / aus einer Datenbank. Die Datenbank fungierte als Datenvermittler zwischen den Kommunikations- und Touchscreen-Benutzeroberflächen. Die App der Touchscreen-Benutzeroberfläche generierte Touchscreen-Schnittstellen basierend auf der Anzahl der an den PC angeschlossenen Monitore (dies wurde automatisch erkannt).
In dem vorgegebenen Zeitrahmen konnte ein Wertepaket zwischen dem Host und unserem PC nur maximal 128 Werte gleichzeitig über das Kabel mit einer maximalen Latenz von ~ 110 ms pro Umlauf senden (UDP wurde mit einer direkten x-over-Ethernet-Verbindung zwischen verwendet die Computer). Die Anzahl der zulässigen Variablen, basierend auf der Anzahl der angeschlossenen Touchscreens, wurde streng kontrolliert. Außerdem hatte der Host (obwohl er eine ziemlich komplexe Multiprozessor-Architektur mit gemeinsam genutztem Speicherbus hat, der für Echtzeit-Computing verwendet wird) ungefähr 1/100 der Verarbeitungsleistung meines Mobiltelefons, sodass er beauftragt wurde, so wenig wie möglich zu verarbeiten und den Server zu verwenden / client musste in der Assembly geschrieben werden, um dies sicherzustellen (auf dem Host wurde eine vollständige Echtzeitsimulation ausgeführt, die von unserem Programm nicht beeinflusst werden konnte).
Das Problem war. Wenn einige Werte auf dem Touchscreen geändert werden, wird nicht nur der neu eingegebene Wert übernommen, sondern es wird nach dem Zufallsprinzip zwischen diesem Wert und dem vorherigen Wert gewechselt. Das und nur auf einigen bestimmten Werten auf einigen bestimmten Seiten mit einer bestimmten Seitenkombination zeigte sich jemals das Symptom. Wir haben das Problem fast komplett verpasst, bis wir damit begonnen haben, den anfänglichen Kundenakzeptanzprozess zu durchlaufen
Um das Problem einzugrenzen, habe ich einen der oszillierenden Werte ausgewählt:
- Ich habe die Touchscreen-App überprüft, sie oszillierte
- Ich überprüfte die Datenbank und oszillierte
- Ich habe die Kommunikations-App überprüft und oszilliert
Dann brach ich Drahthai aus und fing an, die erfassten Pakete manuell zu dekodieren. Ergebnis:
- Nicht oszillierend, aber die Pakete sahen nicht richtig aus, es gab zu viele Daten.
Ich ging hundertmal jedes Detail des Kommunikationscodes durch und fand keinen Fehler.
Schließlich fing ich an, E-Mails an den anderen Entwickler abzusenden und fragte ihn ausführlich, wie sein Ende funktionierte, um festzustellen, ob etwas fehlte. Dann habe ich es gefunden.
Anscheinend hat er beim Senden von Daten das Array von Daten nicht vor der Übertragung geleert, sodass er im Wesentlichen nur den zuletzt verwendeten Puffer überschrieb, wobei die neuen Werte die alten überschrieben, aber die nicht überschriebenen alten Werte noch übertragen wurden.
Wenn sich also ein Wert an Position 80 des Datenfelds befindet und die angeforderte Werteliste auf weniger als 80 geändert wird, derselbe Wert jedoch in der neuen Liste enthalten ist, sind beide Werte zu einem beliebigen Zeitpunkt im Datenpuffer für diesen bestimmten Puffer vorhanden gegebene Zeit.
Der aus der Datenbank gelesene Wert hing von der Zeitscheibe ab, in der die Benutzeroberfläche den Wert anforderte.
Die Lösung war schmerzlich einfach. Lesen Sie die Anzahl der im Datenpuffer eingehenden Elemente ein (sie war tatsächlich als Teil des Paketprotokolls enthalten) und lesen Sie den Puffer nicht über diese Anzahl von Elementen hinaus.
Gewonnene Erkenntnisse:
Nehmen Sie moderne Rechenleistung nicht für selbstverständlich. Es gab eine Zeit, in der Computer kein Ethernet unterstützten und das Leeren eines Arrays als teuer angesehen werden konnte. Wenn Sie wirklich sehen möchten, wie weit wir gekommen sind, stellen Sie sich ein System vor, das praktisch keine Form der dynamischen Speicherzuweisung hat. IE, der ausführende Prozess musste den gesamten Speicher für alle Programme der Reihe nach vorbelegen, und kein Programm konnte über diese Grenze hinauswachsen. Wenn Sie einem Programm mehr Speicher zuweisen, ohne das gesamte System neu zu kompilieren, kann dies zu einem massiven Absturz führen. Ich frage mich, ob die Leute eines Tages im selben Licht über die Tage vor der Müllabfuhr sprechen werden.
Stellen Sie beim Netzwerkbetrieb mit benutzerdefinierten Protokollen (oder beim Umgang mit der Darstellung von Binärdaten im Allgemeinen) sicher, dass Sie die Spezifikation lesen, bis Sie alle Funktionen aller über die Pipe gesendeten Werte verstanden haben. Ich meine, lies es, bis deine Augen weh tun. Menschen, die mit Daten umgehen, indem sie einzelne Bits oder Bytes manipulieren, haben sehr clevere und effiziente Möglichkeiten, Dinge zu tun. Das Fehlen kleinster Details kann das System beschädigen.
Die Gesamtzeit für die Reparatur betrug 2-3 Tage, wobei die meiste Zeit damit verbracht wurde, an anderen Dingen zu arbeiten, als ich frustriert wurde.
SideNote: Der betreffende Host-Computer hat Ethernet standardmäßig nicht unterstützt. Die Karte, mit der das Laufwerk betrieben werden soll, wurde speziell angefertigt und nachgerüstet, und der Protokollstapel war praktisch nicht vorhanden. Der Entwickler, mit dem ich zusammengearbeitet habe, war ein verdammt guter Programmierer. Er hat nicht nur eine abgespeckte Version von UDP und einen minimalen falschen Ethernet-Stack (der Prozessor war nicht leistungsfähig genug, um einen vollständigen Ethernet-Stack zu handhaben) auf dem System für dieses Projekt implementiert aber er tat es in weniger als einer Woche. Er war auch einer der ursprünglichen Projektteamleiter gewesen, der das Betriebssystem überhaupt entworfen und programmiert hatte. Sagen wir einfach, alles, was er jemals über Computer / Programmierung / Architektur zu erzählen hatte, egal wie lange es dauert oder wie viel ich bereits neu bin, würde ich jedem Wort zuhören.