Pregunta

Lo sé, estoy haciendo muchas preguntas ... pero como un nuevo desarrollador de Delphi me caigo sobre todas estas preguntas :)

Este trata de la comunicación TCP usando indy 10. Para hacer que la comunicación sea eficiente, codifico una solicitud de operación del cliente como un solo byte (en la mayoría de los escenarios seguidos de otros bytes de datos, por supuesto, pero en este caso solo un byte). El problema es que

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

no envía ese byte inmediatamente (al menos los servidores ejecutan el controlador no se invoca). Si cambio el '1' a un '9', por ejemplo, todo funciona bien. Asumí que Indy almacena los bytes salientes e intenté desactivar el almacenamiento en búfer con

FConnection.IOHandler.WriteBufferClose;

pero no ayudó. ¿Cómo puedo enviar un solo byte y asegurarme de que se envíe de inmediato? Y, añado otra pequeña pregunta aquí: ¿cuál es la mejor manera de enviar un número entero usando indy? Desafortunadamente no puedo encontrar una función como WriteInteger en el IOHandler de TIdTCPServer ... y

WriteLn (IntToStr (SomeIntVal))

no me parece muy eficiente. ¿Hace una diferencia si uso varios comandos de escritura en una fila o empaquetar cosas en una matriz de bytes y lo envío una vez?

¡Gracias por cualquier respuesta!

EDITAR: Agregué un indicio de que estoy usando Indy 10 ya que parece que hay cambios importantes con respecto a los procedimientos de lectura y escritura.

¿Fue útil?

Solución

El búfer de escritura está deshabilitado por defecto. Puede verificar el almacenamiento en búfer de escritura para ver si está activo en su código probando la propiedad fConnection.IOHandler.WriteBufferingActive.

En cuanto a la mejor manera de enviar un número entero ... 'depende' de su protocolo y objetivos generales. Específicamente, use FConnection.IOHandler.Write () ya que hay métodos sobrecargados para escribir casi cualquier tipo de datos, incluido un entero.

Tomado de 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;

Otra pregunta que tenías era " ¿Hay alguna diferencia si uso varios comandos de escritura en una fila o si empaqueto las cosas en una matriz de bytes y lo envío una vez? " Para la mayoría de los casos, sí, hace una diferencia. Para servidores muy estresados, tendrá que involucrarse más en la forma en que los bytes se envían de un lado a otro, pero a este nivel, debe abstraer sus envíos en una clase de tipo de protocolo separada que genere los datos que se enviarán y los enviará en una explote y tenga un protocolo de recepción que recibe una gran cantidad de datos y los procesa como una unidad completa en lugar de dividir las cosas para enviar / recibir un entero, un carácter, una matriz de bytes, etc.

Como un ejemplo rápido muy aproximado:

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;

Luego envía esto a través de:

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

Por otro lado, puede recibir los datos a través de Indy y luego

  myCommand.Deserialize(ReceivedData);

Otros consejos

No estoy familiarizado con Indy, pero es posible que desee buscar en su API una opción de TCP_NODELAY (es posible que desee grep en el árbol de origen de Indy para algo así: no es sensible a mayúsculas y minúsculas para el "retraso" debería hacerlo. )

Editar: Rob Kennedy señaló que la propiedad a la que me refería es TIdIOHandlerSocket.UseNagle - ¡Gracias!

El problema es inherente a la naturaleza de TCP. TCP garantiza la entrega de datos en el mismo orden en que se emitió, pero no garantiza los límites de los mensajes. En otras palabras, el sistema operativo de la fuente, el destino y cualquier enrutador en el camino son libres de fusionar paquetes desde la conexión o fragmentarlos a voluntad. Debe considerar una transmisión TCP como una transmisión, no como una serie de paquetes individuales. Por lo tanto, deberá implementar un mecanismo mediante el cual puede delimitar los mensajes individuales (por ejemplo, mediante un byte mágico, del cual debe escapar si también puede aparecer en los datos de su mensaje), o puede enviar la longitud del mensaje siguiente. primero, luego el mensaje real.

Siempre he usado UDP junto con un esquema ACK / retransmisión ingenuo cuando necesitaba enviar mensajes donde el límite del mensaje era importante, como es su caso. Tal vez quiera tener eso en cuenta. UDP es mucho más adecuado para los mensajes de comando.

Parece que tienes que vaciar tu búfer. Prueba esto:

TIdTCPConnection.FlushWriteBuffer;

Si no quieres un búfer de escritura, usa esto:

TIdTCPConnection.CancelWriteBuffer;

Según la ayuda, este primero llama a ClearWriteBuffer, para borrar el búfer y luego llama a CloseWriteBuffer.

La mejor forma de enviar un número entero (usando Indy 10) es usar TIdIOHandler.Write (de acuerdo con la ayuda de Indy 10, la escritura está sobrecargada para manejar diferentes tipos de datos, incluidos los números enteros)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top