【问题标题】:Read all available bytes from TCP Socket (unknown byte count)从 TCP Socket 读取所有可用字节(未知字节数)
【发布时间】:2019-04-05 09:57:51
【问题描述】:

我在使用 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 中可用的所有字节?

【问题讨论】:

  • “所有可用字节”是什么意思?您是指在这个特定时刻碰巧可用的所有字节吗?你的实际问题是什么?为什么你认为你需要读取所有可用字节?
  • 是的,我愿意。我正在接收加密数据,这就是为什么我无法读取一个包的长度。问题是我需要在不知道需要读取的数据长度的情况下从socket读取数据。
  • 那么当你得到字节时你打算怎么处理它们呢?它们仍然可能是部分信息。它们可能是一条信息的一部分,也可能是另一条信息的一部分。您需要一些非常复杂的代码来从您收到的数据中提取消息。
  • 我需要把它放在事件的缓冲区中。该事件是 SSH 客户端的一部分。老实说,我还没有想那么多....
  • 那么this question 可能会有所帮助。

标签: sockets c++builder indy indy10 rad-studio


【解决方案1】:

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

使用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=-1 没有采用 @在检查套接字是否有新字节之前考虑 987654337@。

【讨论】:

  • 谢谢,这就像一个魅力。还有一件事,CheckForDataOnSource() 将数据添加到输入缓冲区并且不会替换它,对吗?
  • @Adi 新数据始终附加到InputBuffer 的末尾,保留现有数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多