noch ein kleines dateisenden problem

Für allgemeine Fragen zur Programmierung mit PureBasic.
Moxl
Beiträge: 150
Registriert: 26.10.2012 13:19

noch ein kleines dateisenden problem

Beitrag 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 :)
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7039
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: noch ein kleines dateisenden problem

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Moxl
Beiträge: 150
Registriert: 26.10.2012 13:19

Re: noch ein kleines dateisenden problem

Beitrag 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?
Benutzeravatar
RSBasic
Admin
Beiträge: 8047
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: noch ein kleines dateisenden problem

Beitrag 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.
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7039
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: noch ein kleines dateisenden problem

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Moxl
Beiträge: 150
Registriert: 26.10.2012 13:19

Re: noch ein kleines dateisenden problem

Beitrag von Moxl »

ah gute idee werd ich gleich mal probieren ;)

danke ;)
Moxl
Beiträge: 150
Registriert: 26.10.2012 13:19

Re: noch ein kleines dateisenden problem

Beitrag 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
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7039
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: noch ein kleines dateisenden problem

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Moxl
Beiträge: 150
Registriert: 26.10.2012 13:19

Re: noch ein kleines dateisenden problem

Beitrag 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...
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7039
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: noch ein kleines dateisenden problem

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten