Erstellen eines Packets aus Strukturen

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
deify
Beiträge: 141
Registriert: 29.03.2010 22:01
Computerausstattung: Win7 64bit
AMD Phenom II X4 940
NVIDIA GeForce GTS 250
4GB RAM
Kontaktdaten:

Erstellen eines Packets aus Strukturen

Beitrag von deify »

Hi Leute,
ich wollte wieder einmal ein Serverprogramm schreiben und bin dabei auf folgendes Hinderniss gestoßen. Ich möchte ein Packet zum senden erstellen, dies soll durch eine Procedur erledigt werden, die dann den Pointer zu der Struktur zurückgibt.
Dieser Pointer wird von der nächsten Funktion aufgegriffen und wieder in ein Packet umgewandelt um es schließlich zu versenden.

Hier mal der Code:

Code: Alles auswählen

Procedure createPacket(*sender.user,*receiver.user, type.s, packetData.s)
  Protected *packet.packet
  With *packet
    \sender\id = *sender\id
    \sender\name = *sender\name
    \receiver\id = *receiver\id
    \receiver\name = *receiver\name
    \type = type
    \packetData = packetData
    
  EndWith
  ProcedureReturn *packet
EndProcedure
Die Funktion wird dann wie folgt aufgerufen:

Code: Alles auswählen

sendPacket(createPacket(server,clients(),"info","testinfo"))
sendPacket ist nurnoch die seperate Schlussfunktion zum Senden:

Code: Alles auswählen

Procedure sendPacket(*packet.packet)
  Protected currentpacket.packet : CopyStructure(*packet,@currentpacket,packet)
  SendNetworkData(currentpacket\receiver\id,@currentpacket,1024)
EndProcedure

Jetzt bekomme ich leider schon beim createPacket eine Fehlermeldung, dass der angegebene Zeiger Null ist. Die fehlermeldung kommt nicht wenn ich statt einen pointer in der procedur zu verwenden direkt eine Structur nehme und dann deren pointer zurückgebe, allerdings ist das wohl auch ein falscher Ansatz da somit ja die pointer als Werte eingespeichert würden.

Ich hoffe ihr könnt mir weiterhelfen (:

mfg deify
Windows 7 | 64bit | PureBasic 4.51 (x64/x86)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Erstellen eines Packets aus Strukturen

Beitrag von NicTheQuick »

Du erstellst ja auch innerhalb der Procedure nur einen Pointer und keinen Speicherbereich, der die Daten aufnehmen kann. Du musst das so machen:

Code: Alles auswählen

Procedure createPacket(*sender.user, *receiver.user, type.s, packetData.s)
  Protected *packet.packet = AllocateMemory(SizeOf(packet))
  If Not *packet
  	ProcedureReturn #False
  EndIf
  With *packet
    \sender\id = *sender\id
    \sender\name = *sender\name
    \receiver\id = *receiver\id
    \receiver\name = *receiver\name
    \type = type
    \packetData = packetData
   
  EndWith
  ProcedureReturn *packet
EndProcedure
Jetzt kannst du allerdings nicht mehr einfach nur

Code: Alles auswählen

sendPacket(createPacket(server,clients(),"info","testinfo"))
schreiben, da du sonst ein Speicherleck hast.
Du musst dann den Speicher, der in 'createPacket' erstellt wurde, wieder nach 'sendPacket' frei geben.

In PB ist das leider nicht so einfach wie in C. Es gibt eben keine Scopes.
Benutzeravatar
gnasen
Beiträge: 578
Registriert: 01.08.2007 14:28
Computerausstattung: PB 4.60

Re: Erstellen eines Packets aus Strukturen

Beitrag von gnasen »

Ich habe dir als Ansatz mal den ersten Codeschnippsel ergänzt:

Code: Alles auswählen

Procedure createPacket(*sender.user,*receiver.user, type.s, packetData.s)
  Protected *packet.packet
  
  ;wir brauchen ja auch etwas speicher,
  ;wo wir das ganze hinschreiben können,
  ;also holen wir uns welchen
  *packet = AllocateMemory(SizeOf(packet))
  
  ;haben wir den Speicher bekommen?
  If *packet
    With *packet
      \sender\id = *sender\id
      \sender\name = *sender\name
      \receiver\id = *receiver\id
      \receiver\name = *receiver\name
      \type = type
      \packetData = packetData
    EndWith
  EndIf
  
  ;unbedingt die Rückgabe prüfen, ob das Packet erstellt werden konnte!!!
  ProcedureReturn *packet
EndProcedure
Wichtig ist auch zu beachten, dass alle Strings in PB in einem "pool" rumlungern. Deine Strukturen sehen also quasi so aus:

Code: Alles auswählen

\sender\id = "int"
\sender\name = "pointer auf String = int"
\receiver\id = "int"
\receiver\name = "pointer auf String = int"
...
Der Empfänger kann an diesen Stellen in seinem eigenen Speicher aber die entsprechenden Strings nicht finden. Viel schlimmer noch, er kann von Schundstrings bis IMAs (speicherzugriffs verletzungen) alles entdecken.
Vllt tust es ja bereits (steht leider nicht in deinem Schnippsel), aber versuch lieber fixstrings zu nutzen, zB:

Code: Alles auswählen

test.s{16} = "blabla"
Dies kann wie jede "normale" Variable auch schick verpackt und verschickt werden.
pb 4.51
Benutzeravatar
deify
Beiträge: 141
Registriert: 29.03.2010 22:01
Computerausstattung: Win7 64bit
AMD Phenom II X4 940
NVIDIA GeForce GTS 250
4GB RAM
Kontaktdaten:

Re: Erstellen eines Packets aus Strukturen

Beitrag von deify »

alles klar (: danke euch.
Werde das gleich mal ausprobieren (:

Edit:

TOP danke klappt prima (: hab jetzt den infosting auf 16 und die data auf 1000 begrenzt (: denke so kann man einigermaßen sinnvolle packete verschicken (:
Windows 7 | 64bit | PureBasic 4.51 (x64/x86)
Benutzeravatar
deify
Beiträge: 141
Registriert: 29.03.2010 22:01
Computerausstattung: Win7 64bit
AMD Phenom II X4 940
NVIDIA GeForce GTS 250
4GB RAM
Kontaktdaten:

Re: Erstellen eines Packets aus Strukturen

Beitrag von deify »

Also das versenden klappt super (: Dafür nochmal ein dickes DANKE

Jetzt habe ich versucht eine Pingfunktion einzubauen, die alle 2 Sekunden einen "ping" sendet der wiederum mit einem "OK" beantwortet wird. Das klappt auch super. Wenn der Server offline geht bemerkt der client das auch wie gewollt nach 5 Sekunden.
Jetzt habe ich allerdings dass Problem dass sobald ein 2. Client ins Spiel kommt, der Server das spinnen anfängt. Ich persönlich denke es liegt an der pingfunktion, kann es aber nicht wirklich zuordnen );
Als Fehler kommt das ein Pointer Null ist und zwar in der Funktion createPacket()

Ich gebe euch mal den Code... vielleicht ist die Umsetzung einfach idiotisch oder ihr könnt den Fehler finden (:

server:

Code: Alles auswählen

EnableExplicit

Structure user
  name.s{32}
  id.i
EndStructure

Structure packet
  sender.user
  receiver.user
  type.s{8}
  packetData.s{1000}
EndStructure

#version = "0.2"

Enumeration
  #window
  #statusbar
  
  #chatServerID
  #chatServerPort = 1337
EndEnumeration

Global exit = #False, clients
Global NewList clients.user()
Global server.user
With server
  \id = -1
  \name = "server"
EndWith


If Not InitNetwork()
  MessageRequester("error","network error")
  End
EndIf

Procedure removeClient(clientID)  ;removes client by ID
  ForEach clients()
    If clients()\id = clientID
      DeleteElement(clients())
    EndIf
  Next
EndProcedure

Procedure identifyClient(clientID)  ;identifys client by ID
  ForEach clients()
    If clients()\id = clientID
      ProcedureReturn @clients()
    EndIf
    ProcedureReturn #False
  Next
EndProcedure

Procedure createPacket(*sender.user,*receiver.user, type.s, packetData.s)
  Protected *packet.packet
  *packet = AllocateMemory(SizeOf(packet))
 
  If *packet
    With *packet
      \sender\id = *sender\id
      \sender\name = *sender\name
      \receiver\id = *receiver\id
      \receiver\name = *receiver\name
      \type = type
      \packetData = packetData
    EndWith
  EndIf
  
  ProcedureReturn *packet
EndProcedure

Procedure sendPacket(*packet.packet)
  SendNetworkData(*packet\receiver\id,*packet,SizeOf(packet))
EndProcedure

Procedure clientLogin(loginClient)  ;login procedure (thread)
  Protected *packetBuffer.packet = AllocateMemory(SizeOf(packet))
  clients+1
  Repeat
    ReceiveNetworkData(loginClient,*packetBuffer,SizeOf(packet))
  Until *packetBuffer\type = "login"
  StatusBarText(#statusbar,1,"clients: "+Str(clients))
  AddElement(clients())
  clients()\id = loginClient
  clients()\name = *packetBuffer\sender\name
  StatusBarText(#statusbar,0,"client logged in: "+*packetBuffer\sender\name)
  *packetBuffer = createPacket(server,clients(),"clientID",Str(loginClient))
  If *packetBuffer
    sendPacket(*packetBuffer)
    FreeMemory(*packetBuffer)
  EndIf
EndProcedure

Procedure startChatServer(dummy)  ;network-loop (thread)
  Protected *sender.user, *receiver.user, *packetBuffer.packet = AllocateMemory(SizeOf(packet)), *oldpacket.packet = AllocateMemory(SizeOf(packet))
  
  If CreateNetworkServer(#chatServerID,#chatServerPort,#PB_Network_TCP)
    Repeat
      Select NetworkServerEvent()
        Case #PB_NetworkEvent_Connect
          CreateThread(@clientLogin(),EventClient())
        Case #PB_NetworkEvent_Data
          If ReceiveNetworkData(EventClient(),*packetBuffer,SizeOf(packet))
            If *packetBuffer <> *oldpacket
              *oldpacket = *packetBuffer
              Select *packetBuffer\type
                Case "info"
                Case "ping"
                  *sender = identifyClient(EventClient())
                  If *sender
                    *packetBuffer = createPacket(@server,*sender,"ping","OK")
                    sendPacket(*packetBuffer)
                  EndIf
                Case "msg"
              EndSelect
            EndIf
          EndIf        
        Case #PB_NetworkEvent_Disconnect
          clients-1
          StatusBarText(#statusbar,1,"clients: "+Str(clients))
          removeClient(EventClient())
      EndSelect
      Delay(10)
    Until exit
  EndIf
  CloseNetworkServer(#chatServerID)
  exit = #True
EndProcedure

Procedure windowHandler() ;initializes the window and its eventlistener
  If OpenWindow(#window,0,0,300,20,"networkserver ~ v"+#version,#PB_Window_ScreenCentered|#PB_Window_SystemMenu|#PB_Window_MinimizeGadget)
    If CreateStatusBar(#statusbar,WindowID(#window))
      AddStatusBarField(225)
      AddStatusBarField(75)
      StatusBarText(#statusbar,1,"clients: 0")
      Repeat
        Select WaitWindowEvent()
          Case #PB_Event_CloseWindow
            exit = #True
        EndSelect
      Until exit
    EndIf
  EndIf
EndProcedure

CreateThread(@startChatServer(),0)
windowHandler()

client:

Code: Alles auswählen

EnableExplicit

Structure user
  name.s{32}
  id.i
EndStructure

Structure packet
  sender.user
  receiver.user
  type.s{8}
  packetData.s{1000}
EndStructure

#version = "0.2"

Enumeration
  #Window
  #Send
  #Console
  #Input
  #List
  #Status
  
  #chatServerID
  #chatServerPort = 1337
EndEnumeration

Global exit = #False, chatConnection, IP.s = "127.0.0.1", initialized = #False
Global user.user, server.user
Repeat
  user\name = InputRequester("please enter a nickname","(max. 32 signs, otherwise your name will be cut)","")
Until user\name
server\name = "server"

If Not InitNetwork()
  MessageRequester("Error","Network error")
  End
EndIf

Procedure sendPacket(*packet.packet)
  SendNetworkData(*packet\receiver\id,*packet,SizeOf(packet))
EndProcedure

Procedure createPacket(*sender.user,*receiver.user, type.s, packetData.s)
  Protected *packet.packet
  *packet = AllocateMemory(SizeOf(packet))
 
  If *packet
    With *packet
      \sender\id = *sender\id
      \sender\name = *sender\name
      \receiver\id = *receiver\id
      \receiver\name = *receiver\name
      \type = type
      \packetData = packetData
    EndWith
  EndIf
  
  ProcedureReturn *packet
EndProcedure

Procedure windowHandler() ;initializes the window and its eventlistener
  Protected *packetBuffer = AllocateMemory(SizeOf(packet))
  OpenWindow(#Window, 5, 5, 507, 260, "network client ~ v"+#version+"       "+user\name, #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  AddKeyboardShortcut(#Window,#VK_RETURN,#Send)
  EditorGadget(#Console, 10, 10, 360, 180, #PB_Editor_ReadOnly)
  StringGadget(#Input, 10, 200, 360, 20, "")
  ButtonGadget(#Send, 280, 230, 90, 20, "Send")
  ListViewGadget(#List, 390, 10, 100, 210)
  TextGadget(#Status, 10, 230, 260, 20, "")
  EnableGadgetDrop(#Input,#PB_Drop_Files,#PB_Drag_Copy)
  initialized = #True
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #Send
            If GetGadgetText(#Input)
              *packetBuffer = createPacket(@user,@server,"msg",GetGadgetText(#Input))
              If *packetBuffer
                sendPacket(*packetBuffer)
              EndIf
            EndIf
        EndSelect
      Case #PB_Event_CloseWindow
        exit = #True
    EndSelect
  Until exit
EndProcedure

Procedure startChatConnection(*connectionIP.s)  ;starts the networloop and handles events (thread)
  Protected connectionIP.s = *connectionIP, eventclient, *packetBuffer.packet = AllocateMemory(SizeOf(packet))
  Protected timeout, pingOK = 1
  
  server\id = OpenNetworkConnection(connectionIP,#chatServerPort,#PB_Network_TCP)
  Repeat : Delay(10) : Until initialized
  
  If server\id
    *packetBuffer = createPacket(@user,@server,"login","")                                   ;sending of the login-string
    sendPacket(*packetBuffer)
    
    If ReceiveNetworkData(server\id,*packetBuffer,SizeOf(packet))                            ;receiving clientID
      If *packetBuffer\type = "clientID"
        user\id = Val(*packetBuffer\packetData)
        SetGadgetText(#Console,"Login successful   ID: "+Str(user\id))
        
        
        Repeat                                                                               ;network-slope
          Select NetworkClientEvent(server\id)
            Case #PB_NetworkEvent_Data
              If ReceiveNetworkData(server\id,*packetBuffer,SizeOf(packet))
                Select *packetBuffer\type                                                    ;getting packet-type
                  Case "info"
                  Case "ping"
                    If *packetBuffer\packetData = "OK"
                      pingOK = 1
                      timeout = 0
                    EndIf
                  Case "msg"
                    SetGadgetText(#Console,*packetBuffer\sender\name+": "+*packetBuffer\packetData)
                EndSelect
              EndIf
          EndSelect
          
          If Not timeout
            timeout = ElapsedMilliseconds()
          Else
            If ElapsedMilliseconds()-timeout > 2000 And pingOK = 1
              *packetBuffer = createPacket(@user,@server,"ping","")   
              sendPacket(*packetBuffer)
              pingOK = 0
            EndIf
            If ElapsedMilliseconds()-timeout > 5000
              MessageRequester("error","server timed out")
              exit = #True
            EndIf
          EndIf
          Delay(10)
        Until exit
      Else 
        MessageRequester("error","login failed")
        exit = #True
      EndIf
    Else
      MessageRequester("error","server doesn't answer")
      exit = #True
    EndIf
  Else
    MessageRequester("error","server unavailable")
    exit = #True
  EndIf
  CloseNetworkServer(#chatServerID)
EndProcedure

CreateThread(@startChatConnection(),@ip)
windowHandler()

Danke im Vorraus (:
deify
Windows 7 | 64bit | PureBasic 4.51 (x64/x86)
Sirius-2337
Beiträge: 71
Registriert: 29.05.2010 20:55

Re: Erstellen eines Packets aus Strukturen

Beitrag von Sirius-2337 »

Ich hab mir jetzt nicht alles angesehen aber in deiner identifyClient() Funktion scheint schonmal ein Fehler zu sein.

Code: Alles auswählen

Procedure identifyClient(clientID)  ;identifys client by ID
  ForEach clients()
    If clients()\id = clientID
      ProcedureReturn @clients()
    EndIf
    ProcedureReturn #False
  Next
EndProcedure
Unzwar sollte "ProcedureReturn #False" erst nach der Schleife ausgeführt werden.
Benutzeravatar
deify
Beiträge: 141
Registriert: 29.03.2010 22:01
Computerausstattung: Win7 64bit
AMD Phenom II X4 940
NVIDIA GeForce GTS 250
4GB RAM
Kontaktdaten:

Re: Erstellen eines Packets aus Strukturen

Beitrag von deify »

Danke das wars :D Wie ich solche kleinen Fehler hasse... klein aber weitreichend (:
Ist ja irgendwie auch logisch... er schaut ob der erste die passende ID hat und wenn nicht gibt er gleich false zurück... sprich der 2. client kann garnicht gefunden werden (:


Danke vielmals (:
Windows 7 | 64bit | PureBasic 4.51 (x64/x86)
Antworten