Question

Je sais, je pose beaucoup de questions ... mais en tant que nouveau développeur de Delphi, je tombe constamment dans toutes ces questions:)

Celui-ci traite de la communication TCP en utilisant indy 10. Pour rendre la communication efficace, je code une requête d’opération client sous la forme d’un octet (dans la plupart des scénarios, bien sûr, suivi d’autres octets de données, mais dans ce cas, il n’ya qu’un seul octet). Le problème est que

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

n'envoie pas cet octet immédiatement (au moins, le gestionnaire d'exécution des serveurs n'est pas appelé). Si je change le '1' en '9' par exemple, tout va bien. J'ai supposé qu'Indy tamponne les octets sortants et j'ai essayé de désactiver le tampon d'écriture avec

FConnection.IOHandler.WriteBufferClose;

mais cela n'a pas aidé. Comment puis-je envoyer un seul octet et m'assurer qu'il est envoyé immédiatement? Et - j’ajoute une autre petite question ici - quel est le meilleur moyen d’envoyer un entier en utilisant indy? Malheureusement, je ne trouve pas de fonction similaire à WriteInteger dans IOHandler de TIdTCPServer ... et

.
WriteLn (IntToStr (SomeIntVal))

ne me semble pas très efficace. Le fait d’utiliser plusieurs commandes d’écriture dans une ligne ou de regrouper des éléments dans un tableau d’octets et de les envoyer une seule fois fait-il une différence?

Merci pour vos réponses!

EDIT: J'ai ajouté un indice indiquant que j'utilise Indy 10 car il semble y avoir des changements majeurs concernant les procédures de lecture et d'écriture.

Était-ce utile?

La solution

L'écriture en tampon est désactivée par défaut. Vous pouvez vérifier la mise en mémoire tampon en écriture pour voir si elle est active dans votre code en testant la propriété fConnection.IOHandler.WriteBufferingActive.

En ce qui concerne le meilleur moyen d’envoyer un entier ... "Cela dépend" de votre protocole et de vos objectifs généraux. En particulier, utilisez FConnection.IOHandler.Write () car il existe des méthodes surchargées pour écrire à peu près tout type de données, y compris un entier.

Extrait d'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;

Une autre question que vous avez posée est la suivante: "Est-il important que j'utilise plusieurs commandes d'écriture dans une ligne ou que les éléments soient regroupés dans un tableau d'octets et que je les envoie une fois?" Dans la majorité des cas, oui, cela fait une différence. Pour les serveurs fortement sollicités, vous devrez vous impliquer davantage dans la façon dont les octets sont envoyés, mais à ce niveau, vous devez résumer vos envois dans une classe de type de protocole distinct qui construit les données à envoyer et les envoie dans un fichier. rafale et avoir un protocole de réception qui reçoit un tas de données et le traite comme une unité complète au lieu de se séparer d’envoyer / recevoir un entier, un caractère, un tableau d’octets, etc.

Comme exemple rapide très approximatif:

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;

Envoyez ensuite ceci via:

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

De l'autre côté, vous pouvez recevoir les données via Indy puis

  myCommand.Deserialize(ReceivedData);

Autres conseils

Je ne connais pas Indy, mais vous voudrez peut-être rechercher dans son API une option TCP_NODELAY (vous voudrez peut-être attribuer une valeur similaire à l'arborescence source Indy - les minuscules et les majuscules doivent le faire. )

Modifier: Rob Kennedy a souligné que la propriété à laquelle je faisais référence était TIdIOHandlerSocket.UseNagle - merci!

Le problème est inhérent à la nature de TCP. TCP garantit la livraison des données dans le même ordre qu’il a été émis, mais pas les limites de message. En d’autres termes, le système d’exploitation de la source, de la cible et des routeurs en cours de route sont libres de fusionner les paquets de la connexion ou de les fragmenter à volonté. Vous devez considérer une transmission TCP comme un flux, pas comme une série de paquets individuels. Ainsi, vous devrez implémenter un mécanisme permettant soit de délimiter les messages individuels (par un octet magique, par exemple, que vous devrez échapper si cela peut également apparaître dans vos données de message), ou vous pouvez envoyer la longueur du message suivant. d'abord, ensuite le message réel.

J'ai toujours utilisé le protocole UDP associé à un schéma de retransmission / ACK naïf lorsque je devais envoyer des messages dont la limite de message était importante, comme dans votre cas. Peut-être envie de prendre cela en compte. UDP convient beaucoup mieux aux messages de commande.

On dirait que vous devez vider votre tampon. Essayez ceci:

TIdTCPConnection.FlushWriteBuffer;

Si vous ne voulez pas de tampon d'écriture, utilisez ceci:

TIdTCPConnection.CancelWriteBuffer;

Selon l'aide, cela appelle d'abord ClearWriteBuffer, pour vider le tampon, puis appelle CloseWriteBuffer.

Le meilleur moyen d’envoyer un entier (avec Indy 10) consiste à utiliser TIdIOHandler.Write (selon Indy 10 help, Write est surchargé pour traiter différents types de données, y compris Integers)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top