Seite 1 von 2

noch ein kleines dateisenden problem

Verfasst: 08.12.2012 14:53
von Moxl
Hallo an alle :mrgreen:

also hab hier ein client und ein server code:


server:

Code: Alles auswählen

Global crude$, clientid

Procedure dateiempfangen(param)
  dateiname$=StringField(crude$, 2, Chr(29))
  Size=Val(StringField(crude$, 3, Chr(29)))
  If  OpenWindow(0, 0, 0, 200, 10, dateiname$, #PB_Window_BorderLess)
    ProgressBarGadget(0, 0, 0, 200, 10, 0, Size)
  EndIf
  If Size > 0
     *MemBuffer = AllocateMemory(Size)
     errorcounter = 0 
     ReceivedBytes = 0
     Repeat
      SetGadgetState(0, ReceivedBytes)
      result = ReceiveNetworkData(clientid, *MemBuffer + ReceivedBytes, Size - ReceivedBytes)
      If result = -1
       Delay(1) 
       errorcounter + 1
      Else
       ReceivedBytes + result
       errorcounter = 0
      EndIf
   Until ReceivedBytes = Size Or errorcounter > 6000 ; Abbruch nach 6 Sekunden
  EndIf
  If Size > 0 And ReceivedBytes = Size
    If  CreateFile(0, GetHomeDirectory()+dateiname$)
      WriteData(0, *MemBuffer, Size)
      CloseFile(0)
      FreeMemory(*MemBuffer)
      PrintN(dateiname$+" wurde erfolgreich empfangen.")
      CloseWindow(0)
    Else
      PrintN(dateiname$+" konnte nicht erstellt werden.")
      CloseWindow(0)
    EndIf
  Else
    PrintN(dateiname$+" konnte nicht komplett empfangen werden.")
    CloseWindow(0)
  EndIf
EndProcedure

OpenConsole()
EnableGraphicalConsole(1)
ConsoleTitle("Server")
Delay(500)
Print("Netzwerk wird initialisiert... ")
If  InitNetwork()<>0
  ConsoleColor(14, 0)
  PrintN("initialisiert!")
  ConsoleColor(7, 0)
Else
  ConsoleColor(12, 0)
  PrintN("nicht initialisiert!")
  ConsoleColor(7, 0)
  Input()
  End
EndIf

Print("Server wird gestartet... ")
port=6600
result=CreateNetworkServer(0, port)
If  result=0
  ConsoleColor(12, 0)
  PrintN("Server konnte nicht gestartet werden!")
  ConsoleColor(7, 0)
  Input()
  End
Else
  ConsoleTitle("Server - Port "+Str(port)+"")
  ConsoleColor(14, 0)
  PrintN("gestartet! Port "+Str(port)+"")
  PrintN("")
  ConsoleColor(7, 0)
  
    Repeat
      Delay(20)
      serverevent=NetworkServerEvent()
      
      If  serverevent=#PB_NetworkEvent_Connect
        clientid=EventClient()
        clientid$=Str(clientid)
        clientip$=IPString(GetClientIP(clientid))
        clientport$=Str(GetClientPort(clientid))
        ConsoleColor(2, 0)
        PrintN("Client verbunden: ID: <"+clientid$+">")
        PrintN("IP: <"+clientip$+"> Port: <"+clientport$+">")
        PrintN("")
        ConsoleColor(7, 0)
      EndIf
      
      If  serverevent=#PB_NetworkEvent_Data
        clientid=EventClient()
        clientid$=Str(clientid)
        clientip$=IPString(GetClientIP(clientid))
        buffer.s=Space(2000)
        ReceiveNetworkData(clientid, @buffer, 2000)
        crude$=RTrim(buffer.s)
        
        If  StringField(crude$, 1, Chr(29))="dateisenden"
          CreateThread(@dateiempfangen(), 0)
        EndIf
        
      EndIf
      
      If  serverevent=#PB_NetworkEvent_Disconnect
        clientid=EventClient()
        clientid$=Str(clientid)
        ConsoleColor(4, 0)
        PrintN("Client getrennt: ID: <"+clientid$+">")
        PrintN("")
        ConsoleColor(7, 0)
      EndIf
      
    ForEver
    
EndIf

client:

Code: Alles auswählen

Global verbindung

Procedure dateisenden(param)
  datei$=OpenFileRequester("", "", "Alle Dateien | *.*", 0)
  FF=ReadFile(#PB_Any, datei$)
  If  FF
    dateiname$=GetFilePart(datei$)
    Size.l=Lof(FF)
    *MemBuffer = AllocateMemory(Size)
    ReadData(FF, *MemBuffer, Size)
    SendNetworkString(verbindung, "dateisenden"+Chr(29)+dateiname$+Chr(29)+Str(Size))
    sendbytes=SendNetworkData(verbindung, *MemBuffer, Lof(FF))
    If  sendbytes=Lof(FF)
      PrintN(dateiname$+" wurde komplett gesendet.")
    Else
      PrintN(dateiname$+" konnte nicht komplett gesendet werden.")
    EndIf
    CloseFile(FF)
    FreeMemory(*MemBuffer)
  EndIf
EndProcedure

OpenConsole()
EnableGraphicalConsole(1)
ConsoleTitle("Client")
Delay(500)
Print("Netzwerk wird initialisiert... ")
If  InitNetwork()<>0
  ConsoleColor(14, 0)
  PrintN("initialisiert!")
  ConsoleColor(7, 0)
Else
  ConsoleColor(12, 0)
  PrintN("nicht initialisiert!")
  ConsoleColor(7, 0)
  Input()
  End
EndIf

Print("Verbindung wird hergestellt... ")
host$="localhost"
port=6600
verbindung=OpenNetworkConnection(host$, port)
If  verbindung=0
  ConsoleColor(12, 0)
  PrintN("nicht hergestellt!")
  ConsoleColor(7, 0)
  Input()
  End
Else
  ConsoleTitle("Verbindung mit <"+host$+"> Port: <"+Str(port)+">")
  ConsoleColor(14, 0)
  PrintN("hergestellt! Port "+Str(port)+"")
  PrintN("")
  ConsoleColor(7, 0)
  Print(">")
  Repeat
  input$=Input()
  If  input$<>""
    CreateThread(@dateisenden(), 0)
  EndIf
  ForEver
EndIf
wenn ich jetzt eine datei schicken will dann sendet der die aber beim clienten kommt alles bis auf die letzten paar bytes an :/ könnt es gerne mal selbst ausprobieren.. wenn man die zwei threads allerdigns weg lässt funktionierts :cry: also es liegt irgendwie an den threads...

danke schonmal :)

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 16:16
von STARGÅTE
Algemeine Frage: Hast du Thread-Safe aktiviert?

Zum Code:
Beim Senden führst du folgenden Code aus:

Code: Alles auswählen

SendNetworkString(verbindung, "dateisenden"+Chr(29)+dateiname$+Chr(29)+Str(Size))
sendbytes=SendNetworkData(verbindung, *MemBuffer, Lof(FF))
Nun ist es aber so, dass im Empfangsbuffer, der Dateiinhalt direkt hinter der Zahl für Size steht, bzw. stehen könnte.

Auf der Empfangsseite hast du:

Code: Alles auswählen

ReceiveNetworkData(clientid, @buffer, 2000)
[...]
CreateThread(@dateiempfangen(), 0)
Das heist, du liest beim erste mal 2000 Bytes in den Buffer, dort könnte aber bereits Inhalt der Datei mit drin sein, was du aber unter den Tisch fallen lässt.

An dieser Stelle möchte ich darauf hinweisen:
Wenn man zwei mal SendNetworkData() heißt das nicht dass man auch zwei mal ReceiveNetworkData() zum empfangen benötigt, es kann auch eins reichen oder mehrere nötig sein.
Mit den Befehlen werden keine "Pakete" gesendet, die klar getrennt sind.

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 16:39
von Moxl
also mit threadsafe meinst du bei den compiler optionen das häckchen bei thread-sicheres executable erstellen...
ne hab ich nich^^ aber gilt das nich nur für executables erstellen? hab die prgoramme ja in purebasic ausgeführt und nich erst ne exe erstellt..

und das mit em empfangsbuffer unso glaub ich versteh ich... also ich muss quasi beim clienten den empfangsbuffer 2000 auf

längedesstrings("dateisenden")+längedesstrings(dateiname$)+längedesstrings(str(Size))

ändern? also den empfangsbuffer genau so groß machen wie der string insgesamt ist den ich sende?

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 16:50
von RSBasic
Es ist egal, ob du eine Executable-Datei manuell erstellst oder einfach mit F5 deine Anwendung kompilierst. Es findet trotzdem eine Kompilierung statt, d.h. es wird immer eine Executable-Datei erstellt, ob mit oder ohne Debugger, das ist egal. Daher werden die Compiler-Optionen immer berücksichtigt.

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 16:54
von STARGÅTE
ThreadSafe musst du immer dann aktivieren, wenn du mit Threads arbeitest, auch im Testmodus, wird ja eine EXE erstellt (nur halt temporär).

Zum Empfangsbuffer: Theoretisch ja, aber leider kennt der Client nicht den "noch kommenden" Dateinamen, kann also nicht die Länge des Strings bestimmen.
Also kannst du entweder Fix-Strings nutzen (Dabeiname immer 100Zeichen oder so, und mit SPACE füllen) oder du übergibst deinem Thread noch den ersten Buffer, damit der Thread den erst mal ließt, und dann selber weiter macht.

Ob etwas von der Datei schon drin steckt, kannst du ja Prüfen, wenn du den Rückgabewert des ersten ReceiveNetworkData() mit der Länge des Headers vergleichst, alle überschüssigen Bytes sind dann bereits Teil der Datei.

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 17:10
von Moxl
ah gute idee werd ich gleich mal probieren ;)

danke ;)

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 17:30
von Moxl
also funktioniert soweit... aber wenn die datei komplett empfangen wurde, kommt immer vom debugger eine meldung ungültiger speicherzugriff und dann gehts nichtmehr weiter :/
hier nochmal der code:

server:

Code: Alles auswählen

Global crude$, clientid

Procedure dateiempfangen(param)
  buffer.s=Space(100)
  ReceiveNetworkData(clientid, @buffer, 100)
  dateiname$=RTrim(buffer.s)
  ReceiveNetworkData(clientid, @Size, 4)
  If  OpenWindow(0, 0, 0, 200, 10, dateiname$, #PB_Window_BorderLess)
    ProgressBarGadget(0, 0, 0, 200, 10, 0, Size)
  EndIf
  If Size > 0
     *MemBuffer = AllocateMemory(Size)
     errorcounter = 0 
     ReceivedBytes = 0
     Repeat
      SetGadgetState(0, ReceivedBytes)
      result = ReceiveNetworkData(clientid, *MemBuffer + ReceivedBytes, Size - ReceivedBytes)
      If result = -1
       Delay(1) 
       errorcounter + 1
      Else
       ReceivedBytes + result
       errorcounter = 0
      EndIf
   Until ReceivedBytes = Size Or errorcounter > 6000 ; Abbruch nach 6 Sekunden
  EndIf
  If Size > 0 And ReceivedBytes = Size
    If  CreateFile(0, GetHomeDirectory()+dateiname$)
      WriteData(0, *MemBuffer, Size)
      CloseFile(0)
      FreeMemory(*MemBuffer)
      PrintN(dateiname$+" wurde erfolgreich empfangen.")
      CloseWindow(0)
    Else
      PrintN(dateiname$+" konnte nicht erstellt werden.")
      CloseWindow(0)
    EndIf
  Else
    PrintN(dateiname$+" konnte nicht komplett empfangen werden.")
    CloseWindow(0)
  EndIf
EndProcedure

OpenConsole()
EnableGraphicalConsole(1)
ConsoleTitle("Server")
Delay(500)
Print("Netzwerk wird initialisiert... ")
If  InitNetwork()<>0
  ConsoleColor(14, 0)
  PrintN("initialisiert!")
  ConsoleColor(7, 0)
Else
  ConsoleColor(12, 0)
  PrintN("nicht initialisiert!")
  ConsoleColor(7, 0)
  Input()
  End
EndIf

Print("Server wird gestartet... ")
port=6600
result=CreateNetworkServer(0, port)
If  result=0
  ConsoleColor(12, 0)
  PrintN("Server konnte nicht gestartet werden!")
  ConsoleColor(7, 0)
  Input()
  End
Else
  ConsoleTitle("Server - Port "+Str(port)+"")
  ConsoleColor(14, 0)
  PrintN("gestartet! Port "+Str(port)+"")
  PrintN("")
  ConsoleColor(7, 0)
  
    Repeat
      Delay(20)
      serverevent=NetworkServerEvent()
      
      If  serverevent=#PB_NetworkEvent_Connect
        clientid=EventClient()
        clientid$=Str(clientid)
        clientip$=IPString(GetClientIP(clientid))
        clientport$=Str(GetClientPort(clientid))
        ConsoleColor(2, 0)
        PrintN("Client verbunden: ID: <"+clientid$+">")
        PrintN("IP: <"+clientip$+"> Port: <"+clientport$+">")
        PrintN("")
        ConsoleColor(7, 0)
      EndIf
      
      If  serverevent=#PB_NetworkEvent_Data
        clientid=EventClient()
        clientid$=Str(clientid)
        clientip$=IPString(GetClientIP(clientid))
        buffer.s=Space(2000)
        ReceiveNetworkData(clientid, @buffer, 11)
        crude$=RTrim(buffer.s)
        
        If  crude$="dateisenden"
          CreateThread(@dateiempfangen(), 0)
        EndIf
        
      EndIf
      
      If  serverevent=#PB_NetworkEvent_Disconnect
        clientid=EventClient()
        clientid$=Str(clientid)
        ConsoleColor(4, 0)
        PrintN("Client getrennt: ID: <"+clientid$+">")
        PrintN("")
        ConsoleColor(7, 0)
      EndIf
      
    ForEver
    
EndIf
client:

Code: Alles auswählen

Global verbindung

Procedure dateisenden(param)
  datei$=OpenFileRequester("", "", "Alle Dateien | *.*", 0)
  FF=ReadFile(#PB_Any, datei$)
  If  FF
    filename$=GetFilePart(datei$)
    dateiname$=LSet(filename$, 100)
    dateinamenew$=Left(dateiname$, 100)
    Size.l=Lof(FF)
    SendNetworkString(verbindung, "dateisenden")
    SendNetworkString(verbindung, dateinamenew$)
    SendNetworkData(verbindung, @Size, 4)
    *MemBuffer = AllocateMemory(Size)
    ReadData(FF, *MemBuffer, Size)
    sendbytes=SendNetworkData(verbindung, *MemBuffer, Lof(FF))
    If  sendbytes=Lof(FF)
      PrintN(filename$+" wurde komplett gesendet.")
    Else
      PrintN(filename$+" konnte nicht komplett gesendet werden.")
    EndIf
    CloseFile(FF)
    FreeMemory(*MemBuffer)
  EndIf
EndProcedure

OpenConsole()
EnableGraphicalConsole(1)
ConsoleTitle("Client")
Delay(500)
Print("Netzwerk wird initialisiert... ")
If  InitNetwork()<>0
  ConsoleColor(14, 0)
  PrintN("initialisiert!")
  ConsoleColor(7, 0)
Else
  ConsoleColor(12, 0)
  PrintN("nicht initialisiert!")
  ConsoleColor(7, 0)
  Input()
  End
EndIf

Print("Verbindung wird hergestellt... ")
host$="localhost"
port=6600
verbindung=OpenNetworkConnection(host$, port)
If  verbindung=0
  ConsoleColor(12, 0)
  PrintN("nicht hergestellt!")
  ConsoleColor(7, 0)
  Input()
  End
Else
  ConsoleTitle("Verbindung mit <"+host$+"> Port: <"+Str(port)+">")
  ConsoleColor(14, 0)
  PrintN("hergestellt! Port "+Str(port)+"")
  PrintN("")
  ConsoleColor(7, 0)
  Print(">")
  Repeat
  input$=Input()
  If  input$<>""
    CreateThread(@dateisenden(), 0)
  EndIf
  ForEver
EndIf

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 17:45
von STARGÅTE
Ich verstehe deinen Code leider nicht ganz.
Wozu überhaupt der Thread?

So wie es für mich aussieht, erzeugst du unten den Thread: CreateThread(@dateiempfangen(), 0)
Dann werden aber sowohl im Thread Daten empfangen als auch nach 20ms wieder im Hauptprogramm, denn das läuft ja weiter und bekommt auch wieder das NetworkEvent = #PB_NetworkEvent_Data und "klaut" dann dem Thread wieder einige Bytes.

Vermutlich funktioniert der Code nur überhaupt nur ansatzweise, weil du beide Codes auf dem selben PC ausführst.
Bei einer richtigen Übertragung kommt da nur Müll raus, sry.

Ich gebe ja zu, Netzwerk Programmierung (zumindest in PB) ist nicht einfach. Auch ich habe mit dafür extra ein Include gebaut, damit Übertragungen einfacher und sicherer sind.
Du kannst auch mal im Forum gucken, dort haben einige Ihre Include veröffentlicht.
Ob ich meins auch mal als Alternative veröffentliche weiß ich nicht, aber da immer wieder solche Fragen und Probleme auftauchen wäre es vermutlich an der Zeit.

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 18:45
von Moxl
ja weil das nur ein beispiel code ist... in einem anderen größeren code ist es ja so das mehrere anfragen außer datei senden kommen können... so und wie soll ich die anfragen sonst unterscheiden?
der server muss ja erstma erfahren was genau der server will... ob er nun ne datei schicken will.. oder ihm irgend eine andere info oder sonstwas geben will... deswegen dieser thread.. der ist ja nur zum empfangen einer datei... und davor wird erst geschaut was der client überhaupt vom server will...

Re: noch ein kleines dateisenden problem

Verfasst: 08.12.2012 19:06
von STARGÅTE
Fest steht aber, dass es nur einen Empfangspuffer gibt, es ist also nicht nötig ihn von mehreren Threads auslesen zu lassen.

Für den Typ kannst du erst mal 4 Bytes empfangen, also eine LONG in der drin steht, was da noch so kommt.
In einem nachfolgenden Select-Block kannst du dann je nach Typ weiter darauf reagieren.
In diesen Cases kannst du ja gerne eine Prozedure (nicht Thread) aufrufen, welcher weiter empfängt.
Danach gehts zurück zum Select-Block.

Hier mal n PseudoCode:

Code: Alles auswählen

ReceiveNetworkData(Client, @Type, 4)
Select Type
	Case #Datei
		ReceiveNetworkData(Client, @LaengeDateiName, 4)
	Case #Text
		ReceiveNetworkData(Client, @LaengeText, 4)
EndSelect
Im Netzwerk ist es angenehmer, wenn man immer feste Längen sendet, so also eine Long für den Datentyp der ankommt.
Oder eine Long für die Länge des Dateinamen.
So weißt du immer genau wie viel du noch lesen musst, oder wie Lang der Dateiname ist usw.