简体   繁体   English

Indy写入缓冲/高效TCP通信

[英]Indy Write Buffering / Efficient TCP communication

I know, I'm asking a lot of questions...but as a new delphi developer I keep falling over all these questions :) 我知道,我问了很多问题......但是作为一个新的delphi开发人员,我一直在讨论所有这些问题:)

This one deals with TCP communication using indy 10. To make communication efficient, I code a client operation request as a single byte (in most scenarios followed by other data bytes of course, but in this case only one single byte). 这个涉及使用indy 10的TCP通信。为了使通信高效,我将客户端操作请求编码为单个字节(在大多数情况下,当然后面跟着其他数据字节,但在这种情况下只有一个字节)。 Problem is that 问题是

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

does not send that byte immediately (at least the servers execute handler is not invoked). 不立即发送该字节(至少不调用服务器执行处理程序)。 If I change the '1' to a '9' for example everything works fine. 如果我将'1'更改为'9',例如一切正常。 I assumed that Indy buffers the outgoing bytes and tried to disable write buffering with 我假设Indy缓冲了输出字节,并尝试禁用写缓冲

FConnection.IOHandler.WriteBufferClose;

but it did not help. 但它没有帮助。 How can I send a single byte and make sure that it is immediatly sent? 如何发送单个字节并确保立即发送? And - I add another little question here - what is the best way to send an integer using indy? 并且 - 我在这里添加另一个小问题 - 使用indy发送整数的最佳方法是什么? Unfortunately I can't find function like WriteInteger in the IOHandler of TIdTCPServer...and 不幸的是,我无法在TIdTCPServer的IOHandler中找到像WriteInteger这样的函数......而且

WriteLn (IntToStr (SomeIntVal))

seems not very efficient to me. 对我来说似乎不是很有效率。 Does it make a difference whether I use multiple write commands in a row or pack things together in a byte array and send that once? 我是否在一行中使用多个写命令或在字节数组中将事物打包在一起并发送一次会有所不同吗?

Thanks for any answers! 谢谢你的回答!

EDIT: I added a hint that I'm using Indy 10 since there seem to be major changes concerning the read and write procedures. 编辑:我添加了一个提示,我正在使用Indy 10,因为在读写过程中似乎有重大变化。

Write buffering is disabled by default. 默认情况下禁用写缓冲。 You can check write buffering to see if it's active in your code by testing the fConnection.IOHandler.WriteBufferingActive property. 您可以通过测试fConnection.IOHandler.WriteBufferingActive属性来检查写缓冲以查看它是否在您的代码中处于活动状态。

As far as the best way to send an integer... 'it depends' on your protocol and overall goals. 至于发送整数的最佳方式......“这取决于你的协议和总体目标。 Specifically, use FConnection.IOHandler.Write() as there are overloaded methods to write just about any type of data, including an integer. 具体来说,使用FConnection.IOHandler.Write(),因为有重载方法可以写入任何类型的数据,包括整数。

Taken from IdIOHandler: 取自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;

Another question you had was "Does it make a difference whether I use multiple write commands in a row or pack things together in a byte array and send that once?" 你遇到的另一个问题是“我是否连续使用多个写命令或者在一个字节数组中将所有内容打包在一起并发送一次会有所不同吗?” For the majority of cases, yes it makes a difference. 对于大多数情况,是的,它会产生影响。 For highly stressed servers you are going to have to get more involved in how bytes are sent back and forth, but at this level you should abstract out your sends into a separate protocol type class that builds the data to be sent and sends it in a burst and have a receiving protocol that receives a bunch of data and processes it as a complete unit instead of breaking things down to sending/receiving an integer, character, byte array, etc.. 对于高度紧张的服务器,您将不得不更多地参与如何来回发送字节,但在此级别,您应该将您的发送抽象为一个单独的协议类型类,该类构建要发送的数据并将其发送到突发并有一个接收协议,接收一堆数据并将其作为一个完整的单元处理,而不是分解为发送/接收整数,字符,字节数组等。

As a very rough quick example: 作为一个非常粗略的快速示例:

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;

Then send this via: 然后发送此通过:

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

On the other side you can receive the data via Indy and then 另一方面,您可以通过Indy接收数据

  myCommand.Deserialize(ReceivedData);

I'm not familiar with Indy, but you might want to look around its API for a TCP_NODELAY option (you might want to grep the Indy source tree for something like that - case insensitive for "delay" should do it.) 我不熟悉Indy,但您可能希望查看其API以获取TCP_NODELAY选项(您可能希望grey Indy源代码树以获取类似内容 - 对“延迟”不区分大小写应该这样做。)

Edit: Rob Kennedy pointed out that the property I was referring to is TIdIOHandlerSocket.UseNagle - thanks! 编辑: Rob Kennedy指出我所指的属性是TIdIOHandlerSocket.UseNagle - 谢谢!

The problem is inherent in the nature of TCP. 这个问题是TCP的本质所固有的。 TCP does guarantee data delivery in the same order as it was emitted but does not guarantee message boundaries. TCP确实在因为它被发射的,但并不保证消息边界相同的顺序保证的数据传送。 In other words, the operating system of the source, of the target, and any routers along the way are free to coalesce packets from the connection or to fragment them at will. 换句话说,源的,目标的操作系统和沿途的任何路由器可以自由地从连接合并分组或者随意分段它们。 You must look at a TCP transmission as a stream, not as a series of individual packets. 您必须将TCP传输视为流, 而不是一系列单独的数据包。 Thus you will have to implement a mechanism by which you either delimit the individual messages (by a magic byte, for example, which you must escape if it can also occur in your message data), or you could send the length of the following message first, then the actual message. 因此,您必须实现一种机制,您可以通过该机制分隔单个消息(例如,如果消息数据中也可以出现,则必须通过魔术字节进行分隔),或者您可以发送以下消息的长度首先,然后是实际的消息。

I've always used UDP coupled with a naive ACK/retransmission scheme when I needed to send messages where the message boundary was important, such as is your case. 当我需要发送消息边界很重要的消息时,我总是使用UDP和天真的ACK /重传方案,例如你的情况。 Might want to take that into account. 可能想要考虑到这一点。 UDP is much better suited for command messages. UDP更适合命令消息。

Sounds like you have to flush your buffer. 听起来你必须冲洗你的缓冲区。 Try this: 尝试这个:

TIdTCPConnection.FlushWriteBuffer;

If you don't want a write buffer, use this: 如果您不想写缓冲区,请使用:

TIdTCPConnection.CancelWriteBuffer;

According to the help, this first calls ClearWriteBuffer, to clear the buffer and then calls CloseWriteBuffer. 根据帮助,这首先调用ClearWriteBuffer,清除缓冲区,然后调用CloseWriteBuffer。

The best way to send an integer (using Indy 10) is using TIdIOHandler.Write (according to Indy 10 help Write is overloaded to handle different kinds of data, including Integers) 发送整数(使用Indy 10)的最佳方法是使用TIdIOHandler.Write(根据Indy 10帮助Write重载以处理不同类型的数据,包括整数)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM