首页 > 解决方案 > 从 TCP Socket 读取所有可用字节(未知字节数)

问题描述

我在使用 Indy 时遇到问题TIdTCPClient。如果套接字上有可用的数据,我想调用一个函数。为此,我有一个线程调用IdTCPClient->Socket->Readable(100)。函数本身如下所示:

TMemoryStream *mStream = new TMemoryStream;
int len = 0;
try
{
    if(!Form1->IdTCPClient2->Connected())
        Form1->IdTCPClient2->Connect();
    mStream->Position = 0;
    do
    {
        Form1->IdTCPClient2->Socket->ReadStream(mStream, 1);
    }
    while(Form1->IdTCPClient2->Socket->Readable(100));

    len = mStream->Position;
    mStream->Position = 0;
    mStream->Read(Buffer, len);
}catch(Exception &Ex) {
    Form1->DisplaySSH->Lines->Add(Ex.Message);
    Form1->DisplaySSH->GoToTextEnd();
}
delete mStream;

它不会在线程内直接调用,而是线程触发了一个事件,就是调用了这个函数。这意味着我使用Readable(100)了两次,中间没有读取数据。因此,由于我不知道必须读取多少字节,所以我认为我可以读取一个字节,检查是否有更多可用字节,然后再读取另一个字节。这里的问题是 do while 循环不循环,它只运行一次。我猜 Readable 并不完全符合我的需要。有没有其他方法可以接收 Socket 中可用的所有字节?

标签: socketsc++builderindyindy10rad-studio

解决方案


Readable()在这种情况下,您不应该直接使用。该调用报告底层套接字在其内部内核缓冲区中是否有待处理的未读数据。这并没有考虑到它TIdIOHandler可能已经有未读数据,这些数据InputBuffer是从先前的读取操作中遗留下来的。

使用TIdIOHandler::CheckForDataOnSource()方法而不是TIdIOHandler::Readable()

TMemoryStream *mStream = new TMemoryStream;
try
{
    if (!Form1->IdTCPClient2->Connected())
        Form1->IdTCPClient2->Connect();

    mStream->Position = 0;

    do
    {
        if (Form1->IdTCPClient2->IOHander->InputBufferIsEmpty())
        {
            if (!Form1->IdTCPClient2->IOHander->CheckForDataOnSource(100))
                break;
        }

        Form1->IdTCPClient2->IOHandler->ReadStream(mStream, Form1->IdTCPClient2->IOHandler->InputBuffer->Size, false);

        /* alternatively:
        Form1->IdTCPClient2->IOHandler->InputBuffer->ExtractToStream(mStream);
        */
    }
    while (true);

    // use mStream as needed...
}
catch (const Exception &Ex) {
    Form1->DisplaySSH->Lines->Add(Ex.Message);
    Form1->DisplaySSH->GoToTextEnd();
}
delete mStream;

或者,您也可以使用TIdIOHandler::ReadBytes()代替TIdIOHandler::ReadStream(). 如果将其AByteCount参数设置为-1,它将仅返回当前可用的字节(如果InputBuffer为空,ReadBytes()将等待ReadTimeout套接字接收任何新字节的时间间隔)1

try
{
    if (!Form1->IdTCPClient2->Connected())
        Form1->IdTCPClient2->Connect();

    TIdBytes data;

    do
    {
        if (Form1->IdTCPClient2->IOHander->InputBufferIsEmpty())
        {
            if (!Form1->IdTCPClient2->IOHander->CheckForDataOnSource(100))
                break;
        }

        Form1->IdTCPClient2->IOHandler->ReadBytes(data, -1, true);

        /* alternatively:
        Form1->IdTCPClient2->IOHandler->InputBuffer->ExtractToBytes(data, -1, true);
        */
    }
    while (true);

    // use data as needed...
}
catch (const Exception &Ex) {
    Form1->DisplaySSH->Lines->Add(Ex.Message);
    Form1->DisplaySSH->GoToTextEnd();
}

1:确保您使用的是 Indy 10 的最新快照。在 2016 年 10 月 6 日之前,在检查套接字是否有新字节之前没有考虑ReadBytes()到逻辑错误。AByteCount=-1InputBuffer


推荐阅读