Client/server

Blitz3D Forums/Blitz3D Beginners Area/Client/server

Seldon(Posted 2006) [#1]
Hello,

I remember someone had tried successfully to make a client/server architecture (using TCP/IP) with Blitz (as client) and PureBasic as server.
I wrote a very initial implementation, but I got some problems I can't resolve.

If I run the two programs on ONE computer, it works. Between two computers, the connection is estabilished, but it seems the client sends TWO data packets, instead of one.
Here it is the two sources. I hope you don't mind if one is written in PureBasic.

 
Const MB_OK=      0 
Const MB_ICONERROR=   16 

; --- TIPI DI MESSAGGIO 
Const MSG_NEW=      170 
Const MSG_LIST=      240 
Const MSG_LOGIN=   255 
Const MSG_EVENT=   85 
Const MSG_END=      226 

; --- TIPI DI EVENTO 
Const START=      24 
Const ACCEPT=      129 
Const MOVE=         204 
Const ANSWER=      51 
Const CHAT=         231 
Const GAMEOVER=      7 

; --- CLASSE PLAYER 
Type Player 
   Field Name$ 
   Field IP% 
   Field Enemy_IP% 
   Field FlagFirst ; 1= ha richiesto la partita per primo 
   Field TotalWon 
   Field TotalLost 
End Type 

BUFFER_connessione=CreateBank(1024) 
Dim Utenti$(255) 

Port=6832 
Server_IP$="192.168.1.3" 

Nome_Utente$=Input("User Name: ") 

; si connette al server 
HANDLE_tcp=OpenTCPStream(Server_IP$,Port) 

If Not HANDLE_tcp 
   MessageBox(0,"Impossibile stabilire una connessione con il server TW.","TWClient",MB_OK Or MB_ICONERROR) 
   End 
EndIf 
DebugLog "Connesso." 

; attende l'arrivo del pacchetto MSG_NEW 
ReadBytes(BUFFER_connessione,HANDLE_tcp,0,6) 

DebugLog PeekByte(BUFFER_connessione,0) 
DebugLog PeekInt(BUFFER_connessione,1) 
DebugLog PeekByte(BUFFER_connessione,5) 
Print PeekByte(BUFFER_connessione,0) 
Print PeekInt(BUFFER_connessione,1) 
Print PeekByte(BUFFER_connessione,5) 

c=PeekInt(BUFFER_connessione,1) 

; attende l'arrivo del pacchetto MSG_LIST 
If c>1 
   ReadBytes(BUFFER_connessione,HANDLE_tcp,0,c) 
   l=0 
   For i=1 To c 
      t=PeekByte(BUFFER_connessione,i) 
      If t=35 
         l=l+1 
      Else 
         Utenti$(l)=Utenti$(l)+Chr$(t) 
      EndIf 
      DebugLog PeekByte(BUFFER_connessione,i) 
   Next 
   Print Utenti$(0) 
   Print Utenti$(1) 
   Print Utenti$(2) 
   Print Utenti$(3) 
   Print Utenti$(4) 
   Print Utenti$(5) 
   Print Utenti$(6) 
   Print Utenti$(7) 
   Print Utenti$(8) 
   Print Utenti$(9) 
   Print Utenti$(10) 
   Print Utenti$(11) 
EndIf 

; invia il pacchetto MSG_LOGIN 
DebugLog "MSG_LOGIN ..." 

;tmp$=Chr(MSG_LOGIN)+Nome_Utente$+Chr(MSG_END) 
;WriteString(HANDLE_tcp,tmp$) 
WriteString(HANDLE_tcp,Chr(MSG_LOGIN)+Nome_Utente$+Chr(MSG_END)) 

Print "Batti INVIO per uscire." 
e$=Input() 

CloseTCPStream(HANDLE_tcp) 

End 

 
; --- TIPI DI MESSAGGIO 
#MSG_HELLO=   170 
#MSG_LIST=    240 
#MSG_LOGIN=   -1 
#MSG_EVENT=   85 
#MSG_END=     -30 

; --- TIPI DI EVENTO 
#START=       24 
#ACCEPT=      129 
#MOVE=        204 
#ANSWER=      51 
#CHAT=        231 
#GAMEOVER=    7 

; --- CLASSE PLAYER 
Structure Player 
   Name.s 
   ID.l 
  IP.l 
   Enemy_ID.l 
   Enemy_IP.l 
   FlagFirst.b ; 1= ha richiesto la partita per primo 
   TotalWon.l 
   TotalLost.l 
EndStructure 

OpenConsole() 

InitNetwork() 
ExamineIPAddresses() 
res=NextIPAddress() 
Server_IP.s=IPString(res) 

tmp$=UCase(ProgramParameter()) 
If tmp$="/P" Or tmp$="/PORT" 
  tmp$=ProgramParameter() 
  Port=Val(tmp$) 
  Else 
;  Port=6933 
  Port=6832 
EndIf 
  
If CreateNetworkServer(0,Port) 

  NewList PlayerList.Player() 
  *BUFFER_connessioni=AllocateMemory(1024) 

  OpenWindow(0,0,0,320,240,"Total War Server "+Str(Port),#PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
  
  Repeat 
    WEvent=WindowEvent() 
    If WEvent=#PB_Event_CloseWindow 
      Quit=1 
    EndIf 
    
    SERVER_event=NetworkServerEvent() 
  
    If SERVER_event 
    
      Debug "evento server" 
      
      ID_client.l=EventClient() 

      Select SERVER_event 
        
        Case 1 
        
          Debug "nuova connessione di un client " 

          ; ricava la lista dei giocatori connessi 
          tmp$="" 
          ResetList(PlayerList()) 
          While NextElement(PlayerList()) 
            tmp$+PlayerList()\Name+"#" 
          Wend 
          c=Len(tmp$)+1 
          
          ; invio del pacchetto MSG_HELLO 
          Debug "MSG_HELLO" 
          PokeB(*BUFFER_connessioni,#MSG_HELLO) 
          PokeL(*BUFFER_connessioni+1,c) 
          PokeB(*BUFFER_connessioni+5,#MSG_END) 
          SendNetworkData(ID_client,*BUFFER_connessioni,6) 

          If c>1 
          ; invio del pacchetto MSG_LIST 
            Debug "MSG_LIST" 
            PokeB(*BUFFER_connessioni,#MSG_LIST) 
            PokeS(*BUFFER_connessioni+1,tmp$) 
            PokeB(*BUFFER_connessioni+c,#MSG_END) 
            SendNetworkData(ID_client,*BUFFER_connessioni,c)          
          EndIf 
          
        Case 4 
        
          Debug "chiusa una connessione" 

        Default 
        
          Debug "arrivati dati" 
          PrintN("arrivati dati") 
          
          RequestLength.l=ReceiveNetworkData(ID_client,*BUFFER_connessioni,512) 
          
          Debug "LENGTH= "+Str(RequestLength) 
          PrintN("LENGTH= "+Str(RequestLength)) 
          For a=0 To RequestLength-1 
            Debug PeekB(*BUFFER_connessioni+a) 
            PrintN(Str(PeekB(*BUFFER_connessioni+a))) 
          Next 
          
          Select PeekB(*BUFFER_connessioni+4) 
            
            Case #MSG_LOGIN 
            
              Debug "MSG_LOGIN" 
              PrintN("MSG_LOGIN") 
              
              c=PeekL(*BUFFER_connessioni)-2 
              tmp$=PeekS(*BUFFER_connessioni+5,c) 
              Debug tmp$ 
              PrintN(tmp$) 
              
              Debug ID_client 
              PrintN(Str(ID_client)) 
              IP=GetClientIP(ID_client) 
              Debug IPString(IP) 
              PrintN(IPString(IP)) 
              
              ; aggiunge un nuovo giocatore alla lista          
              AddElement(PlayerList()) 
              PlayerList()\Name=tmp$ 
              PlayerList()\ID=ID_client 
              PlayerList()\IP=IP 
              
            Case #MSG_EVENT 
            
              Debug "MSG_EVENT" 
              
          EndSelect 

         ; Gosub ProcessRequest 

      EndSelect 
    Else 
      Sleep_(20) 
    EndIf 
  Until Quit=1 
    
  CloseNetworkServer(0) 
Else 
  MessageBox_(0,"Non è possibile creare il server.","TWServer",#MB_OK|#MB_ICONERROR) 
EndIf 
  
End 

Basically it works like this: when the server gets a new connection from a client, it sends a MSG_HELLO packet that contains the LENGTH (as LONG) of the further MSG_LIST to be sent, that is a list of all connected user names. In this way the Blitz client waits for the right size of the MSG_LIST packet. Then the client sends a MSG_LOGIN packet with a new user name to be added.

As said, it works when both server and client are on the same computer. I tried on two different computers of my home network, but it doesn't work properly. It seems the server receives two MSG_LOGIN packets.
Here it is a sample of the output of the PB server program :
 
data arrived 
LENGTH=4 
7 
0 
0 
0 
data arrived 
LENGTH=7 
-1 
65 
66 
67 
68 
69 
-30 

The bytes are correct, but I don't understand why it gets two packets.
When you do:
 
WriteString(HANDLE_tcp,Chr(MSG_LOGIN)+Nome_Utente$+Chr(MSG_END)) 

Each Blitz string is stored in the file as a 4 byte integer followed by the characters that form the string. Actually if you look above, 7 is the length of the string. But I can't understand why I get two packets. If I try in local (127.0.0.1), I only get one packet like this:
 
data arrived 
LENGTH=11 
7 
0 
0 
0 
-1 
65 
66 
67 
68 
69 
-30 

May you give me some suggestions ? Thank you VERY MUCH in advance.


markcw(Posted 2006) [#2]
suggestion: have you tried using writeline instead of writestring?


Seldon(Posted 2006) [#3]
Thanks for reply. Actually I tried with WriteBytes() and it gets all bytes in ONE packet. I'll try WriteLine() too just for curiosity.

WriteString() is useful because it adds the length and it's handy (I could it manually though).

I could even live with two packets, but I should be sure it always sends two packets... in local it sends only one packet.