Newsgroup: it.comp.lang.delphi
Titolo: Inspiegabile perdita dati in rete.

Buongiorno a tutti.
Ho creato due applicazioni. Una client e una server.
---------- Client. ---------
Raccoglie dati da una sorgente e li invia al server.
Riceve dal server i dati inviati da altri clients.
--------- Server -----------
Riceve dati dai clients e li transmette a tutti gli altri.
Saltuariamente, invia messaggi ai clients  (tipo smetti di monitorare, 
riprendi a monitarare, mostra questi dati in finestra,....)
--- i clients (in totale 16) mandano decine di messaggi/secondo al 
server. La banda di rete (1000 !!!) a volte raggiunge anche 80/90% 
(controllata con task manager).
Questa applicazione, che adesso funziona alla grande, č nata in Delphi 5, 
ed č installata su macchine tutte P4 3.2ghz.
Ora, una macchina si č rotta ed č stata sostituita con una CPU E2200.
Ho smesso di dormire. Si perdono pacchetti che č una "meraviglia".
Ho prestato un P4 al cliente, ma in cantina non ne ho piů.
Ho provato ad abilitare, sui P4, l' Hyper htrading. Anche in questo caso 
il problema compare come con E2200.
Utilizzo i TServerSocket e TClientSOcket.
Vorrei tornare a sonni tranquilli.
Cordiali saluti.
Giovanni Brambilla
in...@brambo.it
AtcBrambo
--- posted by geoForum on 
http://www.delphiedintorni.it

 Vorrei tornare a sonni tranquilli.
Ma non dici una parola sull'architettura del tuo software: come 
gestisci i messaggi?
Utilizzi un sistema di queuing dei paccehtti?
Come vedi la mia risposta non tiene conto del fattore scatenante, ossia 
l'hardware che e' stato introdotto nella rete di computer che usa la 
tua soluzione software. Questo perchè vorrei prima capire come funziona 
la tua soluzione.
-- 
Morde

Innanzitutto grazie per avermi risposto.
Spero che ci stia tutto.
OnServerError e
OnClientError sono gestiti e non riportano errori.
questa è la mia mail. 
in...@brambo.it
 
-------------------------------------------------------
-------------------------------------------------------
-------------------------------------------------------
--------------------- server --------------------------
-------------------------------------------------------
  object ServerSocket: TServerSocket
    Active = False
    Port = 1234
    ServerType = stNonBlocking
    ThreadCacheSize = 0
    OnClientDisconnect = ServerSocketClientDisconnect
    OnClientRead = ServerSocketClientRead
    OnClientError = ServerSocketClientError
    Left = 5
    Top = 50
  end
procedure TForm1.DispatchMsgExecpt(Msg: string; GotFromSocket: HWND);
var
  g: Integer;
begin
  //Memo2.Lines.Clear;
  //Memo2.Lines.Add('Connessioni attive '+IntToStr
(ServerSocket.Socket.ActiveConnections));
  for g := 0 to Pred(ServerSocket.Socket.ActiveConnections) do begin
   if ((ServerSocket.Socket.Connections[g].Handle<>GotFromSocket)and
       (ServerSocket.Socket.Connections[g].Handle>0)) then begin
    try
      SendTextToSocket(Msg,ServerSocket.Socket.Connections[g],False);
      //AddLogMsg('sending to socket '+IntToStr(GotFromSocket));
     except
      ServerSocket.Socket.Connections[g].Close;
      ServerSocket.Socket.Connections[g].Destroy;
      exit;
    end;
   end;
  end;
end;
procedure TForm1.ServerSocketClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
      procedure DecodeData(Var cInMsg: String; DestSocket: 
TCustomWinSocket);
                Function TakeNextPieceOfString: String;
                begin
                  if (Trim(cInMsg)='')then exit;
                  cInMsg:=Trim(cInMsg)+' ';
                  Result:=Trim(Copy( cInMsg, 1 , Pos(' ',cInMsg)));
                  cInMsg:=Copy( Trim(cInMsg), 1+Pos(' ',cInMsg),Length
(cInMsg));
                end;
      Var i:Integer;
          s:String;
          Header:String[1];
          MsgCopy:String;
      begin
         MsgCopy:=cInMsg;
         Header:=Copy(cInMsg,1,1);
         cInMsg:=Copy(cInMsg,3,Length(cInMsg))+' ';
         // --------- SS_QUIT -----------------------
         if (Header=chr(SS_QUIT))then begin // Client si presenta ....
                                  iPilotIDNr := StrToInt
(TakeNextPieceOfString);
                                  with NetWorkPlanes[iPilotIDNr] do begin
                                    Connected:=False; //Renderable:=False;
                                    iPilotID:=-1;
                                    x:=-1234567.0;
                                    y:=0.0;
                                    z:=-1111111.0;
                                    Heading:=90.0;
                                    Picth:=0.0;
                                    Roll:=0.0;
                                    iAcftType:=-1;
                                  end;
                                  DispatchMsgExecpt
(MsgCopy,Socket.Handle);
         end;
         // --------- SS_START ----------------------
         if (Header=chr(SS_START))then begin // Client si presenta ....
                                  NetWorkPlanes[iPilotIDNr].iAcftType := 
StrToInt(TakeNextPieceOfString);
                                  NetWorkPlanes[iPilotIDNr].CallSign  := 
TakeNextPieceOfString;
                                  NetWorkPlanes[iPilotIDNr].Connected := 
True;
                                  // Mando la nuova identità appena 
connessa
                                  s:= chr(SS_IDENTITY)
+' '+                           // Invio la identità del' ACFT
                                      IntToStr(iPilotIDNr)
+' '+                           // Posizione nel vettore degli ACFT.
                                      IntToStr(NetWorkPlanes
[iPilotIDNr].iAcftType)+' '+  // Tipo dell' ACFT
                                      NetWorkPlanes
[iPilotIDNr].CallSign+' ';             // Call Sign
                                  DispatchMsg(s);
           Exit;
         end;
         // --------- SS_ACFTPOS --------------------
         if (Header=chr(SS_ACFTPOS))then begin
                                  iPilotIDNr := StrToInt
(TakeNextPieceOfString);
                                  NetWorkPlanes[iPilotIDNr].iAcftType := 
StrToInt(TakeNextPieceOfString);
                                  NetWorkPlanes[iPilotIDNr].CallSign  := 
TakeNextPieceOfString;
                                  NetWorkPlanes[iPilotIDNr].Connected := 
True;
                                  // Mando la nuova identità appena 
connessa
                                  NetWorkPlanes[iPilotIDNr].x:= StrToFloat
(TakeNextPieceOfString);
                                  NetWorkPlanes[iPilotIDNr].y:= StrToFloat
(TakeNextPieceOfString);
                                  NetWorkPlanes[iPilotIDNr].z:= StrToFloat
(TakeNextPieceOfString);
                                  //
                                  NetWorkPlanes[iPilotIDNr].IAS:= StrToInt
(TakeNextPieceOfString);
                                  //
                                  NetWorkPlanes
[iPilotIDNr].Roll    :=StrToFloat(TakeNextPieceOfString);
                                  NetWorkPlanes
[iPilotIDNr].Picth   :=StrToFloat(TakeNextPieceOfString);
                                  NetWorkPlanes
[iPilotIDNr].Heading :=StrToFloat(TakeNextPieceOfString);
           DispatchMsgExecpt(MsgCopy,Socket.Handle);//<<<<<<<<<<<<<<<<<<--
-------------------- occhio a questo .....
           Exit;
         end;
      end;
begin
  //
  //ShowConnectivity; // Solo per debug ....
  //
  ClientAddress := Trim(Socket.RemoteAddress); // Indirizzo del Client. 
Da questo valore identifico il nome del computer/posizione in array.
  iPilotIDNr    := IdentifyPilot( '', ClientAddress);
  //
  if (iPilotIDNr<0) then begin // Non è un IP di quelli dedicati ....
   try
     if Assigned(Socket.Data) then Socket.Free;
    except
   end;
   Exit;
  end;
  //
  LastMsg:=(Socket.ReceiveText)+' '; // Lo spazio server per una
  if not Assigned(Socket.Data) then begin // Primo collegamento del 
client .......
   Socket.Data := TClientData.Create(Socket.RemoteAddress);
   //
   NetWorkPlanes[iPilotIDNr].SocketHandle :=Socket.Handle;
   NetWorkPlanes[iPilotIDNr].Connected    :=True;
   AddLogMsg('',NetWorkPlanes[iPilotIDNr].PilotaName+' : Connesso 
('+ClientAddress+') '{+IntToStr(iPilotIDNr)});
   //
  end;
  //
  DecodeData(LastMsg,Socket);
end;
-------------------------------------------------------
-------------------------------------------------------
-------------------------------------------------------
---------------------  client  ------------------------
-------------------------------------------------------
  object ClientSocket: TClientSocket
    Active = False
    Address = '192.168.0.1'
    ClientType = ctBlocking
    Port = 0
    OnConnect = ClientSocketConnect
    OnDisconnect = ClientSocketDisconnect
    OnRead = ClientSocketRead
    OnError = ClientSocketError
    Left = 5
    Top = 5
  end
procedure TForm1.ClientSocketRead(Sender: TObject;
  Socket: TCustomWinSocket);
          procedure ATCS_Decode_Message(Var cInMsg: String);
          Var iArryPos,
              iAcftType,
              iPilotID,
              iState: Integer;
              iSwap_1,iSwap_2: Integer;
              fSwap_1:Extended;
              Header:String;
                           Function TakeNextPieceOfString: String;
                           begin
                             if (Trim(cInMsg)='')then exit;
                             cInMsg:=Trim(cInMsg)+' ';
                             Result:=Trim(Copy( cInMsg, 1 , Pos
(' ',cInMsg)));
                             cInMsg:=Copy( Trim(cInMsg), 1+Pos
(' ',cInMsg),Length(cInMsg));
                           end;
          begin
            //cInMsg:=Copy(cInMsg,2,Length(cInMsg));
            Header:=cInMsg[1];
            //
            cInMsg:=Copy(cInMsg,3,Length(cInMsg))+' ';
            // --------- SS_START -------------------------
            if (Header=chr(SS_START)) then begin // Un ACFT si 
presenta .... cazzi del server ....
             cInMsg:='';
             Exit;
            end;
            // --------- SS_IDENTITY ----------------------
            if (Header=Chr(SS_IDENTITY)) then begin            // Il 
server mi presenta un ACFT .... cazzi miei ....
             iArryPos  :=StrToInt(TakeNextPieceOfString);      // 
Posizione del pilota nell' array ....
             if (iArryPos=I_Am_Pilot_Nr) then  exit;           // Il 
server mi sta presentando me stesso .... esco.
             iAcftType :=StrToInt(TakeNextPieceOfString);      // Numero 
dell' ACFT
             NetACFT[iArryPos].iACFT_Type_Nr  :=AcftModels
[iArryPos].ListID; // Questo è il numero da usare in GlList ....
             NetACFT[iArryPos].CallSign := TakeNextPieceOfString;      // 
Nominativo.
             exit;
            end;
            // --------- SS_CONTROL  ----------------------
            if (Header=Chr(SS_CONTROL)) then begin           // Il server 
mi dice Run/Pause Simulation ....
             iSwap_1  :=StrToInt(TakeNextPieceOfString);     // Valore 
costante
             iSwap_2  :=StrToInt(TakeNextPieceOfString);     // 1=Sim 
Paused.  0=Sim RUN.
             MyAcft.bIsRunning:=(iSwap_2=1);
             //label10.Caption:=IfThen(iSwap_2=0,'Pasua','Running');
             exit;
            end;
            // --------- SS_CONTROL  ----------------------
            // --------- SS_CONTROL  ----------------------
            // --------- SS_QUIT --------------------------
            if (Header=chr(SS_QUIT)) then begin // Ricevo i dati di un 
ACFT.
                        iPilotID:=StrToInt(TakeNextPieceOfString);
                        with NetACFT[iPilotID] do  begin
                         Render:=0;
                         iACFT_Type_Nr:=0;
                         x:=-12345678; y:=x; z:=x;
                         CallSign:='';
                         Bank:=0; Picth:=0; Heading:=0;
                        end;
            end;
            // --------- SS_ACFTPOS -----------------------
            if (Header=chr(SS_ACFTPOS)) then begin // Ricevo i dati di un 
ACFT.
                        {
                        ClientSocket.Socket.SendText(
                                  chr(SS_ACFTPOS)+' '+               // 
Tipo del messaggio ....
                                  IntToStr( I_Am_Pilot_Nr )+' '+     // 
Nella rete, io sono il pilota nr. ....
                                  IntToStr( MyAcft.iAcftType )+' '+  // 
Tipo dell' ACFT
                                  MyAcft.cCallSign+' '+
                                  FormatFloat('#0.0000;-
#0.0000;0.0000',MyAcft.x)+' '+
                                  FormatFloat('#0.0000;-
#0.0000;0.0000',MyAcft.y)+' '+
                                  FormatFloat('#0.0000;-
#0.0000;0.0000',MyAcft.z)+' '+
                                  // In questa linea ci sono solo dati 
per il radar istruttore ....
                                  FormatFloat('000;-000;000',MyAcft.IAS)
+' '+
                                  FormatFloat('#0.000;-
#,0.000;0.0',MyAcft.Bank)+' '+
                                  FormatFloat('#0.000;-
#,0.000;0.0',MyAcft.Pitch)+' '+
                                  FormatFloat('#0.000;-
#,0.000;0.0',MyAcft.Hdg)+' '
                                  );
                        }
                        //Label10.Caption:='Ricevo '+cInMsg;
                        iPilotID:=StrToInt(TakeNextPieceOfString);
                        if (iPilotID<>I_Am_Pilot_Nr)then begin
                                  NetACFT[iPilotID].iACFT_Type_Nr := 
StrToInt(TakeNextPieceOfString);
                                  NetACFT[iPilotID].CallSign      := 
TakeNextPieceOfString;
                                  // ----
                                  // Mando la nuova identità appena 
connessa
                                  NetACFT[iPilotID].x:= StrToFloat
(TakeNextPieceOfString);
                                  NetACFT[iPilotID].y:= StrToFloat
(TakeNextPieceOfString);
                                  NetACFT[iPilotID].z:= StrToFloat
(TakeNextPieceOfString);
                                  //
                                  fSwap_1:=StrToFloat
(TakeNextPieceOfString); //  Valore della IAS
                                  //
                                  NetACFT[iPilotID].Bank    := StrToFloat
(TakeNextPieceOfString);
                                  NetACFT[iPilotID].Picth   := StrToFloat
(TakeNextPieceOfString);
                                  NetACFT[iPilotID].Heading := StrToFloat
(TakeNextPieceOfString);
                                  //
                                  NetACFT[iPilotID].Render        := 1;
                           end else begin
                                  NetACFT[iPilotID].Render        := 0;
                        end;
            end;
          end;
Var s:String;
begin
   s:=Trim(Socket.ReceiveText);
   ATCS_Decode_Message(s);
   //Memo1.Items.Add(s);
   //if Memo1.Items.Count>33 then Memo1.Items.Delete(0);
   //application.ProcessMessages;
end;
--------------- fine del teso -----------------------


Errore. Il client è 
ClientType = stNonBlocking


 AtcBrambo
purtroppo sono sommerso dall'assistenza ai clienti, e non posso dare 
un'occhio attento ai tuoi sorgenti. scorrendo velocemente, vedo che 
mancano righe di log. non so se hai ripulito, comunque ti suggerisco di 
mettere i log e loggare tutto quello che succede, eccezioni comprese, 
perchè con un buon log ti si apre il cielo e potrai vedere di nuovo il 
sole..
non scherzo, mettici dei buoni log ed analizza l'output.
Fai sapere sul ng i tuoi progressi.
Ciao
-- 
Morde

Sarà.... ma prima andava tutto così bene....
Ci provo.
Fornite assistenza a pagamento ???
Grazie.


 Fornite assistenza a pagamento ???
ahaha... :) beh qui dentro è tutto gratis, usenet docet: oggi io aiuto 
te, domani tu aiuterai me, e si cresce entrambi.
Metti i log e vedi cosa succede ai messaggi, se li perdi, se c'e' un 
collo di bottiglia, ecc...
Facci sapere
Buon lavoro, a presto
-- 
Morde

Ho seguito il consiglio di MORDE.
Ho loggato tutto quanto e mi sono accordo che, nonostante le stringhe che 
invio siano separatamente, vengono ricevute tutte insieme; solo 
saltuariamente sono separate.
Ne consegue che ricevo una stringa che in realtà contiene valori di dati 
di almeno 2 ( in realtà fino a dieci !!!!!) stringhe(ossia 10 invii di 
dati differenti). 
Quindi si perdono dati. Ma perchè sul pentium 4 (escludendo in BIOS l' 
HT),questo non succedeva????
Per ottenere il risulato voluto devo mettere uno Sleep(2). Ma poichè si 
tratta di simulazione real time, questo non è ammissibile....
Cercasi aiuto.
in...@brambo.it
Grazie.
AtcBrambo


 HT),questo non succedeva????
per me e' dovuto alla bufferazione, devi provare a svuotare tutti i buffer
quando scrivi nel soket in invio qualcosa come fflush(allstreams)


 quando scrivi nel soket in invio qualcosa come fflush(allstreams)
Credo anche io.. nonostante non abbia mai implementato nulla di simile, 
normalmente in modelli di utilizzo ad alto traffico di pacchetti il 
ricevente si costruisce una coda di baffering..
Ciao
-- 
Morde

 tratta di simulazione real time, questo non ï¿œ ammissibile....
Hai fatto progressi?
Ho verificato sulla mia collezione di The Delphi Magazine e ho visto 
che c'ï¿œ un interessante articolo che potrebbe aiutarti a riguardo, ï¿œ 
sul numero 59, "IBSec: Securing InterBase Network Traffic On The Fly" 
(
http://www.thedelphimagazine.com/conts/conts59.php
) ovviamente 
l'argomento del db non ï¿œ di tuo interesse ma puoi vedere come la 
tecnica di base sia basata su sistemi di buffering o accodamento dei 
pacchetti nel caso il server non sia pronto.
Se non l'hai giï¿œ, ti consiglio di comperare la collezione di TDM, te la 
spediscono su chiavetta usb e hai un patrimonio di articoli e codici 
pronti all'uso. 
http://www.thedelphimagazine.com/coll.php
Girando in rete ho trovato anhce questo articolo:
http://www.delphi3000.com/articles/article_1473.asp?SK=
Ciao
-- 
Morde

Non esistono comandi per svuotare il buffer. Almeno non con i componenti 
che uso io (TServerSocket e TCLientSocket).
Non tirate in ballo gli indy che richiedono modalità blocking e, a meno 
di artifici con salto carpiato, mi fanno crollare il frame rate da circa 
800 a 200 !!! 
Infatti 800 sono sel volte che la strinag di dati parte dal client verso 
il server e lui ra manda agli altri 16 pc della rete.
Veramente un lavoro immane.
Comunque, non c'è modo, tranne che chiudere la connessione ogni volta 
che i dati vengono inviati, di svuotare il buffer. Magari esistesse il 
COMMIT ALL !!!!
Comunque vi potrà confortare che la tramsissione invia tutto il buffer 
in un sol botto.
Esempio se invio 10+30+5+40 caratteri, quando partono lo fanno:
10, or
10+30 or
10+30+5 ecc..
Il trucco sta ad inviare sempre la stessa lunghezza o a codificare un 
header per la lunghezza da leggere.
Non c'è altro modo.
PS. Per il Sig. MORDE...
La cosa strana è che sul pentium 4 andava tutto così bene ....
(La limgua batte dove il dente duole ,... ahh ahha ) Ciao a tutti.


Ho riletto la mia ultima risposta. Purtoppo non c'è compliatore per gli 
errori di battitura ... scusate tutte le brutture del messaggio 
precedente.
and, moreover ....
Se vi viene in mente una dritta, io sono sempre qui ....


Beccato !!!!
Sfogliando i sorgenti ho trovato il metodo write(lenght) che fa 
tipo "commit".
Quindi, dopo avere scritto sul socket, usate sempre 
socket.sendText(Text);
write(Lengt(Text));
Questione chiusa.
Giovanni Brambilla


 write(Lengt(Text));
Grazie del feedback :)
Ciao!
-- 
Morde