Probleme mit Netzwerk

Für allgemeine Fragen zur Programmierung mit PureBasic.
Kooky
Beiträge: 47
Registriert: 18.09.2004 16:13
Wohnort: München
Kontaktdaten:

Probleme mit Netzwerk

Beitrag 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
Benutzeravatar
Konne
Beiträge: 764
Registriert: 30.03.2005 02:20
Kontaktdaten:

Beitrag von Konne »

Also Threads sind in PB eh absolut unzuverlässig, daher würde ich es mal ohne versuchen, also die Funktionen Parsen.
Nik
Beiträge: 132
Registriert: 04.02.2005 19:57

Beitrag von Nik »

info(PeekS(todo()\buffer+1),2)
Ungeschützte Strings in Threads OhOh
www.KoMaNi.de
Eine kleine Gruppe von Hobby Programmierern, die gerade einen Instant Messenger natürlich in PureBasic schreiben.
Kooky
Beiträge: 47
Registriert: 18.09.2004 16:13
Wohnort: München
Kontaktdaten:

Beitrag 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?
Benutzeravatar
freedimension
Admin
Beiträge: 1987
Registriert: 08.09.2004 13:19
Wohnort: Ludwigsburg
Kontaktdaten:

Beitrag 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 <)
Beginne jeden Tag als ob es Absicht wäre!
Bild
BILDblog
Kooky
Beiträge: 47
Registriert: 18.09.2004 16:13
Wohnort: München
Kontaktdaten:

Beitrag von Kooky »

okay thx
Also kann man prinzipiell die Sache mit Netzwerk + Threads vergessen?

mfg. Kooky
Benutzeravatar
Andre
PureBasic Team
Beiträge: 1765
Registriert: 11.09.2004 16:35
Computerausstattung: MacBook Core2Duo mit MacOS 10.6.8
Lenovo Y50 i7 mit Windows 10
Wohnort: Saxony / Deutscheinsiedel
Kontaktdaten:

Beitrag von Andre »

@Kooky: jein, auf PB4.0 warten oder schau Dich zwecks Thread-Sicherheit mal nach "Critical Sections" im Forum um.
Bye,
...André
(PureBasicTeam::Docs - PureArea.net | Bestellen:: PureBasic | PureVisionXP)
Team100
Beiträge: 104
Registriert: 13.09.2004 22:59

Beitrag 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
Kompliziert kann es jeder lösen, aber das wirklich Geniale ist einfach.....
Kooky
Beiträge: 47
Registriert: 18.09.2004 16:13
Wohnort: München
Kontaktdaten:

Beitrag 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:
Antworten