8

我知道,我问了很多问题……但作为一个新的 delphi 开发人员,我一直在思考所有这些问题 :)

这个处理使用 indy 10 的 TCP 通信。为了提高通信效率,我将客户端操作请求编码为单个字节(在大多数情况下,当然后面是其他数据字节,但在这种情况下只有一个单个字节)。问题是

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

不会立即发送该字节(至少不调用服务器执行处理程序)。例如,如果我将“1”更改为“9”,一切正常。我假设 Indy 缓冲传出字节并尝试禁用写缓冲

FConnection.IOHandler.WriteBufferClose;

但它没有帮助。如何发送单个字节并确保立即发送?而且 - 我在这里添加另一个小问题 - 使用 indy 发送整数的最佳方式是什么?不幸的是,我在 TIdTCPServer 的 IOHandler 中找不到像 WriteInteger 这样的函数......和

WriteLn (IntToStr (SomeIntVal))

对我来说似乎不是很有效率。我是连续使用多个写命令还是将内容打包在一个字节数组中并发送一次,这有什么不同吗?

感谢您的任何回答!

编辑:我添加了一个提示,我正在使用 Indy 10,因为关于读写过程似乎有重大变化。

4

3 回答 3

6

默认情况下禁用写缓冲。您可以通过测试 fConnection.IOHandler.WriteBufferingActive 属性来检查写入缓冲以查看它是否在您的代码中处于活动状态。

至于发送整数的最佳方式……“这取决于”您的协议和总体目标。具体来说,使用 FConnection.IOHandler.Write() 因为有重载方法可以写入几乎任何类型的数据,包括整数。

取自 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;

您遇到的另一个问题是“我是连续使用多个写入命令还是将内容打包在一个字节数组中并发送一次,这有什么不同吗?” 在大多数情况下,是的,它会有所作为。对于压力很大的服务器,您将不得不更多地参与字节如何来回发送,但在这个级别,您应该将您的发送抽象为一个单独的协议类型类,该类构建要发送的数据并将其发送到突发并有一个接收协议,它接收一堆数据并将其作为一个完整的单元进行处理,而不是将事情分解为发送/接收整数、字符、字节数组等。

作为一个非常粗略的快速示例:

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;

然后通过以下方式发送:

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

另一方面,您可以通过 Indy 接收数据,然后

  myCommand.Deserialize(ReceivedData);
于 2009-03-04T03:38:53.707 回答
4

我对 Indy 不熟悉,但您可能想查看它的 API 以获取 TCP_NODELAY 选项(您可能需要对 Indy 源代码树进行 grep 查找类似的东西 - “延迟”应该不区分大小写。)

编辑:Rob Kennedy指出我所指的属性是TIdIOHandlerSocket.UseNagle- 谢谢!

这个问题是 TCP 的本质所固有的。TCP 确实保证数据传输的顺序与发出时相同,但不保证消息边界。换句话说,源操作系统、目标操作系统以及沿途的任何路由器都可以自由地合并来自连接的数据包或随意对它们进行分段。您必须将 TCP 传输视为流,而不是一系列单独的数据包。因此,您将必须实现一种机制,通过该机制您可以分隔各个消息(例如,通过一个魔术字节,如果它也可能出现在您的消息数据中,您必须转义它),或者您可以发送以下消息的长度首先,然后是实际消息。

当我需要发送消息边界很重要的消息时(例如您的情况),我总是使用 UDP 和简单的 ACK/重传方案。可能要考虑到这一点。UDP 更适合用于命令消息。

于 2009-03-03T09:33:50.727 回答
0

听起来您必须刷新缓冲区。试试这个:

TIdTCPConnection.FlushWriteBuffer;

如果您不想要写缓冲区,请使用:

TIdTCPConnection.CancelWriteBuffer;

根据帮助,这首先调用ClearWriteBuffer,清除缓冲区,然后调用CloseWriteBuffer。

发送整数(使用 Indy 10)的最佳方式是使用 TIdIOHandler.Write (根据 Indy 10 的帮助,Write 被重载以处理不同类型的数据,包括整数)

于 2009-03-03T09:30:12.077 回答