Seite 1 von 1

Probleme mit Netzwerk

Verfasst: 18.12.2005 18:00
von Kooky
Hey,
ich hab vor ein Remote-Desktop Programm zu schreiben, womit ich meine Windows Server fernsteuern kann.
Jetzt hab ich erstmal angefangen mit dem Programmieren des Netzwerkes,
hab damit aber große Probleme:
- Pakete gehen verloren
- Pakete kommen scheinbar doppelt an
- Pakete kommen in falscher Reihenfolge an
- Mit Debugger geht der Code gar nicht oder besser gesagt, eher "zufällig" und der gibt total komische Fehlermeldungen an.

So also irgendwie geht da gar nichts. Vom System her habe ich das so Programmiert:
- Server wird gestartet
- Dieser startet 2 Threads
- 1. Thread: Schleife, die Ereignisse im Netzwerk überprüft und auswertet. Wenn z.B. ein Paket eintrifft, so fügt dieser Thread den Inhalt des Paketes in eine Liste hinzu.
- 2. Thread: Dieser Bearbeitet die Liste und löscht bearbeitete Pakete bzw. die Elemente der Liste

Wenn der Client lokal auf den Server connected (127.0.0.1 ) gehts noch halbwegs, aber wenn es wirklich übers Netzwerk läuft, dann kommt jedes 2 Paket nicht an.

Servercode:

Code: Alles auswählen

OpenConsole()

; OnErrorSupport
Procedure error()
  Select MessageRequester("Warning","Ein Fehler ist in der Zeile "+Str(GetErrorLineNR())+" aufgetreten",#MB_ICONWARNING | #MB_ABORTRETRYIGNORE)
    Case 3
      End
    Case 4
      error()
  EndSelect
EndProcedure
OnErrorGosub(@error())

; Initialisieren
Declare NetworkHandle()
Declare ServerHandle()
#buffersize = 1000
Global quit.b
InitNetwork()

; Einfacher Messagehandler
Procedure Info(text.s,typ.b)
  Select typ.b
    Case 0
      ;Debug "Error: "+text
      PrintN("Error:    "+text)
      ;MessageRequester("Error",text.s,#MB_ICONSTOP)
    Case 1
      ;Debug "Log: "+text
      PrintN("Log:      "+text)
      ;MessageRequester("Info",text.s,#MB_ICONINFORMATION)
    Case 2
      ;Debug "Nachricht: "+text
      PrintN("Nachricht:"+text)
      ;MessageRequester("Nachricht",text.s,#MB_ICONINFORMATION)
  EndSelect
EndProcedure

; Startet Server
Procedure RunServer()
  If CreateNetworkServer(2000)
    Structure computer
      id.l
    EndStructure
    Structure work ; Struktur für die Liste für das Abarbeiten der Pakete
      id.l
      *buffer
      doit.b
    EndStructure
    Global client.computer
    NewList ToDo.work()
    CreateThread(@ServerHandle(),0)
    CreateThread(@NetworkHandle(),0)
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure ServerHandle() ; Procedure für das Empfangen von Paketen (Thread)
  Repeat
    Select NetworkServerEvent()
      Case 1
        Client\id = NetworkClientID()
      Case 2 ; Wenn ein Paket empfangen wird
        AddElement(Todo()) ; Eine Element wird in die Liste zu Abarbeitung hinzugefügt
        todo()\id = NetworkClientID()
        todo()\buffer = AllocateMemory(#buffersize) ; Speicher für das Paket wird reserviert
        ReceiveNetworkData(todo()\id ,Todo()\buffer,#buffersize)
        Todo()\doit = 1 ; Paket wird für die Bearbeitung freigegeben.
      Case 4
        Client\id = 0
      Default
        Delay(1)
    EndSelect
  Until quit
EndProcedure

Procedure NetworkHandle() ; Procedure für das Bearbeiten der Pakete (Thread)
  Repeat
    If CountList(Todo()) ; Wenn ein Paket für die Bearbeitung da ist
      FirstElement(Todo()) ; Wird das erste Paket ausgewählt
      If Todo()\doit ; Falls dieses Element für die Bearbeitung freigegeben ist
        Select PeekB(todo()\buffer) ; Lese den ersten Byte aus ( hier sollte der immer 1 sein...später soll das sozusagen ein Befehl sein. z.B. ist 1 eine Nachricht und 2 Koordinaten und 3...)
          Case 1 ; Falls 1 => Nachricht
            info(PeekS(todo()\buffer+1),2) ; Liest die Nachricht und bringt sie auf den Bildschirm
        EndSelect
        FreeMemory(Todo()\buffer) ; Fertig bearbeitet => Speicher wieder freigeben
        DeleteElement(Todo()) ; Element löschen
        ResetList(Todo()) ; Liste reseten
      EndIf
     Else
      Delay(1)
    EndIf
  Until quit
EndProcedure


;Hauptroutine

If RunServer()
  info("Server wurde gestartet",1)
  While quit = 0
    Delay(10)
  Wend
  info("Server wurde gestoppt",1)
 Else
  Info("Server konnte nicht gestartet werden",0)
EndIf
End
Clientcode:

Code: Alles auswählen

OpenConsole()
InitNetwork()
*buffer = AllocateMemory(100)
PokeB(*buffer,1) ; Schreibt den ersten Byte, der für den Befehl steht ( hier ist dieser 1 und bedeutet, das es sich um eine Nachricht handelt )
sid.l = OpenNetworkConnection("127.0.0.1",2000)
If sid
  For x = 0 To 100
    PokeS(*buffer+1,Space(50)) ; Leert den Text für die Nachricht
    PokeS(*buffer+1,Str(x)) ; Schreibt den Text
    PrintN(PeekS(*buffer+1)) ; Schreibt den Text, der gesendet wird in die Konsole
    SendNetworkData(sid,*buffer,50) ; Verschickt das Paket ( 1+$Text )
    Delay(1)
  Next
  CloseNetworkConnection(sid)
 Else
  PrintN("Konnte Server nicht finden")
EndIf
PrintN("Finish")
Input()
; IDE Options = PureBasic v3.94 (Windows - x86)
; CursorPosition = 11
; Folding = -
; DisableDebugger
Probiert bitte beide Codes bitte mal mit und ohne debugger.

Danke im Vorraus.

mfg. Kooky

Verfasst: 18.12.2005 18:43
von Konne
Also Threads sind in PB eh absolut unzuverlässig, daher würde ich es mal ohne versuchen, also die Funktionen Parsen.

Verfasst: 18.12.2005 18:48
von Nik
info(PeekS(todo()\buffer+1),2)
Ungeschützte Strings in Threads OhOh

Verfasst: 18.12.2005 20:24
von Kooky
Nik hat geschrieben:
info(PeekS(todo()\buffer+1),2)
Ungeschützte Strings in Threads OhOh
huh?

Ja und was sollte ich anders machen?

Verfasst: 18.12.2005 20:35
von freedimension
Kooky hat geschrieben:
Nik hat geschrieben:
info(PeekS(todo()\buffer+1),2)
Ungeschützte Strings in Threads OhOh
huh?
Threads sind nicht stringsicher ... äh, andersrum, Strings sind nicht threadsicher <)

Verfasst: 18.12.2005 22:50
von Kooky
okay thx
Also kann man prinzipiell die Sache mit Netzwerk + Threads vergessen?

mfg. Kooky

Verfasst: 19.12.2005 00:35
von Andre
@Kooky: jein, auf PB4.0 warten oder schau Dich zwecks Thread-Sicherheit mal nach "Critical Sections" im Forum um.

Verfasst: 19.12.2005 01:10
von Team100
ReceiveNetworkData() liefert ein Ergebnis, nämlich die tatsächlich
in den Speicher gelesene Bytezahl.

Stimmt die erhaltene Bytezahl mit der erwarteten nicht überein,
muß die Abfrage in einer Schleife wiederholt werden, solange bis
die erwartete Bytezahl da ist. Fehlabfragen quittiert ReceiveNetworkData
mit -1.

Ist die zu erwartende Bytezahl dem Server nicht bekannt, muß sie
vorab in einem Header vom Client mitgeteilt werden. Der Header
sollte eine fixe vereinbarte Länge haben, sodaß er einfach ausgewertet
werden kann.

Hintergrund ist, daß naturgemäß jede Netzwerkübertragung Zeitverzögerungen beinhalten kann
und sich der Empfangsbuffer nur entsprechend füllt.

Sinnvollerweise stellt ReceiveNetworkData() erhaltene Daten bereits
unmittelbar zur Verfügung.

Ebenso sollte man SendNetworkData() auf den Rückgabewert (= gesendete Bytes)
abfragen, auch hier kann es dazu kommen, daß nicht alle zum Absenden
gedachten Daten auf einmal in den Sendespeicher geschrieben werden
können.

Cu von Team100

Verfasst: 19.12.2005 08:17
von Kooky
Andre hat geschrieben:@Kooky: jein, auf PB4.0 warten oder schau Dich zwecks Thread-Sicherheit mal nach "Critical Sections" im Forum um.
<)
Team100 hat geschrieben:ReceiveNetworkData() liefert ein Ergebnis, nämlich die tatsächlich
in den Speicher gelesene Bytezahl.

Stimmt die erhaltene Bytezahl mit der erwarteten nicht überein,
muß die Abfrage in einer Schleife wiederholt werden, solange bis
die erwartete Bytezahl da ist. Fehlabfragen quittiert ReceiveNetworkData
mit -1.

Ist die zu erwartende Bytezahl dem Server nicht bekannt, muß sie
vorab in einem Header vom Client mitgeteilt werden. Der Header
sollte eine fixe vereinbarte Länge haben, sodaß er einfach ausgewertet
werden kann.

Hintergrund ist, daß naturgemäß jede Netzwerkübertragung Zeitverzögerungen beinhalten kann
und sich der Empfangsbuffer nur entsprechend füllt.

Sinnvollerweise stellt ReceiveNetworkData() erhaltene Daten bereits
unmittelbar zur Verfügung.

Ebenso sollte man SendNetworkData() auf den Rückgabewert (= gesendete Bytes)
abfragen, auch hier kann es dazu kommen, daß nicht alle zum Absenden
gedachten Daten auf einmal in den Sendespeicher geschrieben werden
können.

Cu von Team100
Okay. Jetzt schaff ich das schon. Ich machs jetzt erstmal ohne Threads.
Vielen Dank für eure Hilfe

mfg. Kooy :wink: