Client/Server in einem Programm, wie geht das mit den Event

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Bisonte
Beiträge: 2468
Registriert: 01.04.2007 20:18

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von Bisonte »

In Ermangelung einer ordentlichen Dokumentation (nicht jeder hat "Zusi") ist es uns unmöglich ein Beispiel zu generieren,
das sich zumindest mit dem Server verbinden kann, da wir nicht GENAU wissen, was der Server für Datenpakete erwünscht
oder verschickt.

Das Beispiel, schnell aus der PB-Hilfe (Network - Client.pb) geschraubt, soll es stark vereinfacht zeigen
wie es gehen könnte...

Code: Alles auswählen

If InitNetwork() = 0
  MessageRequester("Error", "Can't initialize the network !", 0)
  End
EndIf

#ACK_HELLO = $00010102
#SV_ACK_HELLO = $000200

Procedure SendStringPaket(ConnectionID, Typ, String.s)
  
  Protected *Buffer, i
  
  If Len(String)>0 And Len(String)<256 ; String muss eine Länge zwischen 0 und 256 Zeichen haben
        
    *Buffer = AllocateMemory(StringByteLength(String)+4) ; Speicher anmelden (1 Long und Stringkennung + StringLänge)
    
    If *Buffer ; Wenn Speicher anmelden erfolgreich
      
      PokeL(*Buffer, Typ) ; Den Typ "Poken" z.B. ACK_Hello
      
      PokeB(*Buffer+4, StringByteLength(String)) ; Länge des Strings an den Anfang "Poken"
      
      For i = 1 To Len(String) ; Den String dahinter "Poken") ohne das CHR(0) am Ende
        
        PokeB(*Buffer + 4 + i, Asc(Mid(String, i, 1)))
        
      Next i
      
      If ConnectionID ; Ist die Verbindung gültig ?
        SendNetworkData(ConnectionID, *Buffer, MemorySize(*Buffer)) ; Senden des Strings  
      EndIf
      
      FreeMemory(*Buffer) ; Speicher wieder freigeben
      
    EndIf
    
  EndIf
    
EndProcedure

*Empfang = AllocateMemory(100) ; Speicher anmelden um Daten zu Empfangen.
; Keine Doku, deswegen k.A. wieviel Empfangsbuffer gebraucht wird (Maximale Grösse in Bytes)

Port = 1435 ; Der Port des Zusi Servers
Adresse.s = "127.0.0.1" ; Die IP Adresse des ZUSI Servers ("127.0.0.1" entspricht localhost)

ConnectionID = OpenNetworkConnection(Adresse, Port)

If ConnectionID
  
  Debug "Ich bin mit dem Server verbunden."
  
  ; Hier das Hallo senden : FP > SRV 00 01 01 02 08 46 61 68 72 70 75 6C 74 HELLO-Befehl
  
  SendStringPaket(ConnectionID, #ACK_HELLO, "MeinPRG")
  
  ; Jetzt auf Antwort warten
  
  RBytes = ReceiveNetworkData(ConnectionID, *Empfang, MemorySize(*Empfang))
  
  If PeekL(*Empfang) = #SV_ACK_HELLO
    Debug "Server hat Ok gemeldet"
  EndIf
  
  CloseNetworkConnection(ConnectionID)
Else
  Debug "Kann den Server nicht finden"
EndIf
  
End
PureBasic 6.21 (Windows x86/x64) | Windows11 Pro x64 | AsRock B850 Steel Legend Wifi | R7 9800x3D | 64GB RAM | GeForce RTX 5080 | ThermaltakeView 270 TG ARGB | build by vannicom​​
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: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von NicTheQuick »

Das Senden von Daten ist so nicht ganz korrekt, weil der Rückgabewert von 'SendNetworkData()' nicht überprüft wurde.
So ist es richtig:

Code: Alles auswählen

Procedure.i SendNetworkDataEx(ConnectionID.i, *Buffer, length.i)
	Protected offset.i = 0, sentBytes.i
	While length = offset
		sentBytes = SendNetworkData(ConnectionID, *Buffer + offset, length - offset)
		If sentBytes = -1
			Delay(10)
		Else
			offset + sentBytes
		EndIf
	Wend
	ProcedureReturn length
EndProcedure


Procedure.i SendStringPaket(ConnectionID.i, Typ.l, string.s)
	Protected *Buffer, stringLength.i = Len(string), realStringLength.i = StringByteLength(string)
	
	If stringLength >= 0 And stringLength < 256 ; String muss eine Länge zwischen 0 und 256 Zeichen haben
		SendNetworkDataEx(ConnectionID, @Typ, SizeOf(Typ))
		SendNetworkDataEx(ConnectionID, realLength, SizeOf(Byte))
		SendNetworkDataEx(ConnectionID, @string, realStringLength)
		ProcedureReturn #True
	EndIf
	
	ProcedureReturn #False
EndProcedure
///Edit:
Code korrigiert.
funkheld
Beiträge: 649
Registriert: 31.12.2009 11:58

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von funkheld »

Danke nochmals für eure unermüdliche Mühe.

Gruss
Zuletzt geändert von funkheld am 07.02.2012 15:18, insgesamt 1-mal geändert.
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: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von NicTheQuick »

Dann mach's doch einfach so:

Code: Alles auswählen

Procedure.i SendNetworkDataEx(ConnectionID.i, *Buffer, length.i)
	Protected offset.i = 0, sentBytes.i
	While length = offset
		sentBytes = SendNetworkData(ConnectionID, *Buffer + offset, length - offset)
		If sentBytes = -1
			Delay(10)
		Else
			offset + sentBytes
		EndIf
	Wend
	ProcedureReturn length
EndProcedure

DataSection
	HELLO_ZUSI:
		Data.a $00, $01, $01, $02, $08, $46, $61, $68, $72, $70, $75, $6C, $74 
EndDataSection

;Zu Server verbinden

SendNetworkDataEx(ConnectionID, ?HELLO_ZUSI, 13)
///Edit:
Code korrigiert.
funkheld
Beiträge: 649
Registriert: 31.12.2009 11:58

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von funkheld »

Jup, danke. Eigentlich läuft alles korrekt ab.
Aber es wird im Zusi-Server als unbekannter Client ausgewertet.

Ich frage mich, warum macht man das eigentlich vom Clienten zum Zusi-Server und davon zum Programm Zusi.
Der Zusi-Server ist ja nur der Zusi-Datenverteiler für das Fahrpult(Purebasic-Client) und die Infozelle(Info-Client).

Ich habe jetzt ein Protokoll, wo man einfach die Daten vom Zusi-Programm empfangen kann, welches eigentlich der Zusi-Server benutzt und die Daten wieder verteilt an das Fahrpult und die Info-Zelle. Das brauche ich ja nicht.

Ich kann ja die Daten direkt empfangen mit dem Purebasic-Server(SRV) und verarbeiten.
Jetzt ist wieder das Problem, wie kann ein Server senden und empfangen???

Das ist zb das Prokoll, wenn sich der Zusi-Server(SRV) mit dem Zusiprogramm(ZUSI) unterhält.
Der Zusi-Server(SRV) kann doch jetzt das Purebasicprogramm(SRV) sein , welches die unterhaltung unten durchführt dann braucht man nicht noch einen dritten dazwischen schalten
ZUSI > SRV 00 01 01 01 04 5A 75 73 69
HELLO-Befehl
Protokoll-Version "1"
Client-Typ "Zusi"
String "Zusi" mit führender Längenangabe (4 Bytes)

SRV > ZUSI 00 02 00 ACK_HELLO-Befehl
Befehl akzeptiert

SRV > ZUSI 00 03 00 0A 01 02 03 NEEDED_DATA-Befehl
Befehlsvorrat 00 0A
IDs 01, 02 und 03 (dieses sind die Datentypen nach einem Schlüssel 01 zb Druck , 02 zb Geschwindigkeit)

ZUSI > SRV 00 04 00 ACK_NEEDED_DATA-Befehl
Befehl akzeptiert

SRV > ZUSI 00 03 00 00 NEEDED_DATA-Befehl
Befehlsvorrat 00 00: Letzter Befehl

ZUSI > SRV 00 04 00 ACK_NEEDED_DATA-Befehl
Befehl akzeptiert

ZUSI > SRV 00 0A 01 04 6B 74 81
02 A2 4E 67 91
03 02 12 01 00
DATA-Befehl
Daten-IDs 01-03 Daten (konvertierter Single-Datentyp)
Wenn ich euch auf dem Wecker falle, schreibt das ruhig.
Vielleicht ist dieses die Einfache Lösung.

Ich habe hier mal ein Probeprogramm, wenn ich im Zusiprogramm die Datenverbindung starte, dann bekomme ich die gesendeten Daten : ZUSI > SRV 00 01 01 01 04 5A 75 73 69 korrekt.

Also Purebasic kann die Daten vom Zusiprogramm gut empfangen.
Nur eben wie kann dann das Purebasicprogramm eine Unterhaltung führen mit dem Zusiprogramm ?

Es nervt mich zur Zeit, das ich dieses überhaupt angefangen habe und euch damit Nerve.

Mein einfaches Chaosprogramm welches die Daten auf der Console Printet, ich kann aber keine Antwort zurücksenden :

Code: Alles auswählen

Global z.l

OpenConsole()

InitNetwork() 

*Buffer = AllocateMemory(1000)

If CreateNetworkServer(0, 1435)
  
  Repeat
      
    SEvent = NetworkServerEvent()
  
    If SEvent
      ClientID = EventClient()
  
      Select SEvent    
        Case #PB_NetworkEvent_Connect
          PrintN("client hat sich gemeldet...")
          
        Case #PB_NetworkEvent_Data
          PrintN("client "+Str(ClientID)+" hat gesendet...")
          PrintN("")
          ReceiveNetworkData(ClientID, *Buffer, 40)
          For z=0 To 8
            PrintN(StrU(PeekB(*Buffer+z)))
          Next z
          PrintN("")
          For z=9 To 40
            Print(Chr(PeekB(*Buffer+z)))
          Next z
          
        Case #PB_NetworkEvent_Disconnect
          PrintN("client "+Str(ClientID)+" geschlossen...")
          Quit = 1  
      EndSelect
      
    EndIf 
  Until Quit = 1 
 
  CloseNetworkServer(0)
Else
  PrintN("kann server nicht erstellen")
  Input()
EndIf

End 
- chris -
Beiträge: 195
Registriert: 24.08.2005 19:52
Wohnort: Stadtallendorf

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von - chris - »

Hallo

Bei mir funtioniert das so, die Länge der Daten davor setzen:

Code: Alles auswählen


If InitNetwork() = 0
  End
EndIf

*sbuffer = AllocateMemory(1024)
If *sbuffer = 0
  End
EndIf

*rbuffer = AllocateMemory(1024)
If *rbuffer = 0
  End
EndIf

verb = OpenNetworkConnection("127.0.0.1", 1435)

Debug "verb: " + Str(verb)
Debug ""

If verb

  ;00 01 01 02 08 46 61 68 72 70 75 6C 74

  offs = 0

  PokeL(*sbuffer + 0, $00) : offs + 4
  PokeA(*sbuffer + 4, $00) : offs + 1
  PokeA(*sbuffer + 5, $01) : offs + 1
  PokeA(*sbuffer + 6, $01) : offs + 1
  PokeA(*sbuffer + 7, $02) : offs + 1
 
  client$ = "Fahrpult"   
  PokeA(*sbuffer + 8, Len(client$)) : offs + 1
  PokeS(*sbuffer + 9, client$, -1, #PB_Ascii) : offs + Len(client$)
 
  ;Länge der Daten
  PokeL(*sbuffer + 0, offs - 4)
 
  zeile$ = ""
  For n = 0 To offs - 1
    zeile$ = zeile$ + RSet(Hex(PeekA(*sbuffer + n)), 2, "0")
    If n < offs - 1
      zeile$ = zeile$ + ":"
    EndIf
  Next n
  Debug zeile$

  sData = SendNetworkData(verb, *sbuffer, offs)
  Debug "sData: " + Str(sData)

  t1 = ElapsedMilliseconds()
  Repeat
    CEvent = NetworkClientEvent(verb)
    t2     = ElapsedMilliseconds()
    If (t2 - t1) > 5000
      Break
    EndIf
    Delay(10)
  Until CEvent <> 0

  Debug ""

  If CEvent <> 0

    rData = ReceiveNetworkData(verb, *rbuffer, 1024)

    Debug "rData: " + Str(rData)

    zeile$ = ""
    For n = 0 To rData - 1
      zeile$ = zeile$ + RSet(Hex(PeekA(*rbuffer + n)), 2, "0")
      If n < rData - 1
        zeile$ = zeile$ + ":"
      EndIf
    Next n
    Debug zeile$

  EndIf

  Delay(10000)

  CloseNetworkConnection(verb)

EndIf

End

Code: Alles auswählen


verb: 3214776

0D:00:00:00:00:01:01:02:08:46:61:68:72:70:75:6C:74
sData: 17

rData: 7
03:00:00:00:00:02:00

Bild
PB v5.72 x86/x64
Windows 10 Pro 64bit
funkheld
Beiträge: 649
Registriert: 31.12.2009 11:58

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von funkheld »

Au man..., ich bedanke mich bei dir, das du so durchgehalten hast mit meiner vielen fragerei.
Und dein Test, ... ! Danke.

Frage: warum kommt als Antwort nicht nur die letzten 3 Byte, was bedeuten die ersten 4 Byte ?

Danke.

Gruss
- chris -
Beiträge: 195
Registriert: 24.08.2005 19:52
Wohnort: Stadtallendorf

Re: Client/Server in einem Programm, wie geht das mit den Ev

Beitrag von - chris - »

Die ersten 4 Byte, sind die Länge der Daten als Long.

03:00:00:00

ergibt die Zahl 3.
PB v5.72 x86/x64
Windows 10 Pro 64bit
Antworten