Domanda

Lo so, sto facendo molte domande ... ma come nuovo sviluppatore di delphi continuo a cadere su tutte queste domande :)

Questo si occupa della comunicazione TCP utilizzando indy 10. Per rendere efficiente la comunicazione, codifico una richiesta di operazione client come un singolo byte (nella maggior parte degli scenari seguita ovviamente da altri byte di dati, ma in questo caso solo un singolo byte). Il problema è che

var Bytes : TBytes;
...
SetLength (Bytes, 1);
Bytes [0] := OpCode;
FConnection.IOHandler.Write (Bytes, 1);
ErrorCode := Connection.IOHandler.ReadByte;

non invia immediatamente quel byte (almeno non viene invocato il gestore di esecuzione del server). Se cambio '1' in '9' per esempio, tutto funziona bene. Ho ipotizzato che Indy buffer i byte in uscita e ho provato a disabilitare il buffer di scrittura con

FConnection.IOHandler.WriteBufferClose;

ma non ha aiutato. Come posso inviare un singolo byte e assicurarmi che sia inviato immediatamente? E - aggiungo un'altra piccola domanda qui - qual è il modo migliore per inviare un numero intero usando indy? Purtroppo non riesco a trovare una funzione come WriteInteger in IOHandler di TIdTCPServer ... e

WriteLn (IntToStr (SomeIntVal))

non mi sembra molto efficiente. Fa differenza se uso più comandi di scrittura in una riga o impacco le cose in un array di byte e le invio una volta?

Grazie per qualsiasi risposta!

EDIT: ho aggiunto un suggerimento che sto usando Indy 10 poiché sembrano esserci importanti cambiamenti riguardanti le procedure di lettura e scrittura.

È stato utile?

Soluzione

Il buffering di scrittura è disabilitato per impostazione predefinita. Puoi controllare il buffering di scrittura per vedere se è attivo nel tuo codice testando la proprietà fConnection.IOHandler.WriteBufferingActive.

Per quanto riguarda il modo migliore per inviare un numero intero ... "dipende" dal protocollo e dagli obiettivi generali. In particolare, utilizzare FConnection.IOHandler.Write () in quanto esistono metodi sovraccarichi per scrivere praticamente qualsiasi tipo di dati, incluso un numero intero.

Tratto da IdIOHandler:

// Optimal Extra Methods
//
// These methods are based on the core methods. While they can be
// overridden, they are so simple that it is rare a more optimal method can
// be implemented. Because of this they are not overrideable.
//
//
// Write Methods
//
// Only the ones that have a hope of being better optimized in descendants
// have been marked virtual
procedure Write(const AOut: string; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure WriteLn(const AEncoding: TIdEncoding = enDefault); overload;
procedure WriteLn(const AOut: string; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure WriteLnRFC(const AOut: string = ''; const AEncoding: TIdEncoding = enDefault); virtual;
procedure Write(AValue: TStrings; AWriteLinesCount: Boolean = False; const AEncoding: TIdEncoding = enDefault); overload; virtual;
procedure Write(AValue: Byte); overload;
procedure Write(AValue: Char; const AEncoding: TIdEncoding = enDefault); overload;
procedure Write(AValue: LongWord; AConvert: Boolean = True); overload;
procedure Write(AValue: LongInt; AConvert: Boolean = True); overload;
procedure Write(AValue: SmallInt; AConvert: Boolean = True); overload;
procedure Write(AValue: Int64; AConvert: Boolean = True); overload;
procedure Write(AStream: TStream; ASize: Int64 = 0; AWriteByteCount: Boolean = False); overload; virtual;

Un'altra domanda che hai posto è stata " Fa differenza se uso più comandi di scrittura in una riga o impacco le cose in un array di byte e le invio una volta? " Nella maggior parte dei casi, sì, fa la differenza. Per i server altamente stressati dovrai essere più coinvolto nel modo in cui i byte vengono inviati avanti e indietro, ma a questo livello dovresti sottrarre le tue invii in una classe di tipo di protocollo separata che costruisce i dati da inviare e li invia in un scoppiare e avere un protocollo di ricezione che riceve un sacco di dati e li elabora come un'unità completa invece di suddividere le cose per inviare / ricevere un numero intero, un carattere, un array di byte, ecc.

Come esempio rapido molto approssimativo:

TmyCommand = class(TmyProtocol)
private
  fCommand:Integer;
  fParameter:String;
  fDestinationID:String;
  fSourceID:String;
  fWhatever:Integer;
public
  property Command:Integer read fCommand write fCommand;
  ...

  function Serialize;
  procedure Deserialize(Packet:String);
end;

function TmyCommand.Serialize:String;
begin
  //you'll need to delimit these to break them apart on the other side
  result := AddItem(Command) + 
            AddItem(Parameter) + 
            AddItem(DestinationID) + 
            AddItem(SourceID) + 
            AddItem(Whatever);
end; 
procedure TMyCommand.Deserialize(Packet:String);
begin
   Command := StrToInt(StripOutItem(Packet));
   Parameter := StripOutItem(Packet);
   DesintationID := StripOutItem(Packet); 
   SourceID := StripOutItem(Packet);
   Whatever := StrToInt(StripOutItem(Packet));
end;

Quindi invialo tramite:

  FConnection.IOHandler.Write(myCommand.Serialize());

Dall'altro lato è possibile ricevere i dati tramite Indy e quindi

  myCommand.Deserialize(ReceivedData);

Altri suggerimenti

Non ho familiarità con Indy, ma potresti voler cercare nella sua API un'opzione TCP_NODELAY (potresti desiderare di greppare l'albero dei sorgenti di Indy per qualcosa del genere: maiuscole e minuscole per " delay " dovresti farlo. )

Modifica: Rob Kennedy ha sottolineato che la proprietà a cui mi riferivo è TIdIOHandlerSocket.UseNagle - grazie!

Il problema è inerente alla natura del TCP. TCP garantisce la consegna dei dati nello stesso ordine in cui è stato emesso ma non garantisce i limiti dei messaggi. In altre parole, il sistema operativo della sorgente, della destinazione e tutti i router lungo la strada sono liberi di unire i pacchetti dalla connessione o frammentarli a piacimento. È necessario considerare una trasmissione TCP come flusso, non come una serie di singoli pacchetti. Pertanto, dovrai implementare un meccanismo mediante il quale delimitare i singoli messaggi (ad esempio un byte magico, al quale è necessario sfuggire se può verificarsi anche nei dati del messaggio) oppure inviare la lunghezza del messaggio seguente prima, poi il messaggio reale.

Ho sempre usato UDP in combinazione con un ingenuo schema ACK / ritrasmissione quando avevo bisogno di inviare messaggi in cui il limite del messaggio era importante, come nel tuo caso. Potrebbe voler tenerne conto. UDP è molto più adatto ai messaggi di comando.

Sembra che devi svuotare il buffer. Prova questo:

TIdTCPConnection.FlushWriteBuffer;

Se non si desidera un buffer di scrittura, utilizzare questo:

TIdTCPConnection.CancelWriteBuffer;

Secondo l'aiuto, questo prima chiama ClearWriteBuffer, per cancellare il buffer e quindi chiama CloseWriteBuffer.

Il modo migliore per inviare un numero intero (usando Indy 10) è usare TIdIOHandler.Write (secondo la guida di Indy 10 la scrittura è sovraccarica per gestire diversi tipi di dati, inclusi i numeri interi)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top