【问题标题】:Indy10, IdTCPClient's IOHandler send additional dataIndy10,IdTCPClient的IOHandler发送附加数据
【发布时间】:2014-08-22 01:25:13
【问题描述】:

试图将数据发送到另一台机器。我使用 C++Builder XE6 和 Indy10。

TMemoryStream *sms = new TMemoryStream();

sms->Write(msgData, msgSize);

Form1->IdTCPClient1->IOHandler->WriteBufferOpen();
Form1->IdTCPClient1->IOHandler->Write(sms, 0, true);
Form1->IdTCPClient1->IOHandler->WriteBufferFlush();

delete sms;

当我使用 Wireshark 检查发送数据时,我将附加数据和 msgData 的数据发送到机器。 附加数据为“00 00 00 20”,在发送数据的头部。

IdTcpClient 通常会发送这样的附加数据吗?

【问题讨论】:

    标签: indy10


    【解决方案1】:

    它正在发送额外的数据,因为您将Write(TStream)AWriteByteCount 参数设置为true。 00 00 00 20 是网络字节顺序的流大小。 msgSize 是 0x00000020,也就是 32。如果您不希望发送流大小,则需要将 AWriteByteCount 参数设置为 false:

    Form1->IdTCPClient1->IOHandler->Write(sms, 0, false);
    

    此外,您应该使用WriteBufferClose() 而不是WriteBufferFlush(),如果Write() 引发异常,请不要忘记调用WriteBufferCancel()WriteBufferClose() 将缓冲的数据发送到套接字,然后关闭缓冲区,因此不会缓冲后续写入。 WriteBufferFlush() 将缓冲的数据发送到socket,但不关闭缓冲区,因此后续的写入会被缓冲。

    此外,您可以通过将TMemoryStream 替换为TIdMemoryBufferStream 来稍微简化开销,这样您就不必在内存中制作单独的消息数据副本:

    TIdMemoryBufferStream *sms = new TIdMemoryBufferStream(msgData, msgSize);
    try
    {
        Form1->IdTCPClient1->IOHandler->WriteBufferOpen();
        try
        {
            Form1->IdTCPClient1->IOHandler->Write(sms, 0, true); // or false
            Form1->IdTCPClient1->IOHandler->WriteBufferClose();
        }
        catch (const Exception &)
        {
            Form1->IdTCPClient1->IOHandler->WriteBufferCancel();
            throw;
        }
    }
    __finally
    {
        delete sms;
    }
    

    或者,使用 RAII 方法:

    class BufferIOWriting
    {
    private:
        TIdIOHandler *m_IO;
        bool m_Finished;
    
    public:
        BufferIOWriting(TIdIOHandler *aIO) : m_IO(aIO), m_Finished(false)
        {
            IO->WriteBufferOpen();
        }
    
        ~BufferIOWriting()
        {
            if (m_Finished)
                m_IO->WriteBufferClose();
            else
                m_IO->WriteBufferCancel();
        }
    
        void Finished()
        {
            m_Finished = true;
        }
    };
    
    {
        std::auto_ptr<TIdMemoryBufferStream> sms(new TIdMemoryBufferStream(msgData, msgSize));
        BufferIOWriting buffer(Form1->IdTCPClient1->IOHandler);
        Form1->IdTCPClient1->IOHandler->Write(sms.get(), 0, true); // or false 
        buffer.Finished();
    }
    

    话虽如此,我建议完全摆脱写入缓冲:

    TIdMemoryBufferStream *sms = new TIdMemoryBufferStream(msgData, msgSize);
    try
    {
        Form1->IdTCPClient1->IOHandler->Write(sms, 0, true); // or false 
    }
    __finally
    {
        delete sms;
    }
    

    或者:

    {
        std::auto_ptr<TIdMemoryBufferStream> sms(new TIdMemoryBufferStream(msgData, msgSize));
        Form1->IdTCPClient1->IOHandler->Write(sms.get(), 0, true); // or false 
    }
    

    当您需要进行多个相关的Write() 调用时,写缓冲很有用,这些调用应该在尽可能少的 TCP 帧中一起传输它们的数据(换句话说,让 Nagle 算法更好地完成它的工作)。例如,如果您需要 Write() 消息的各个字段。在进行单个 Write() 调用时,使用写缓冲没有多大意义,尤其是在如此小的尺寸下。让Write(TStream) 在内部为您处理自己的缓冲。

    【讨论】:

    • 当我尝试第一个和第三个答案时,我以网络字节顺序和 msgData 发送流大小。会发生什么?
    • 您在调用TIdIOHandler.Write() 时将AWriteByteCount 参数设置为true。如果您不希望发送流大小,则需要将参数设置为 false
    • 感谢您的建议。我改把参数设置为false,我只给机器发送了msgData。
    猜你喜欢
    • 2020-03-05
    • 1970-01-01
    • 2012-02-19
    • 2011-09-13
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    相关资源
    最近更新 更多