Чтение потока данных с использованием TIdIOHandlerStream и TIdTCPClient

У меня есть приложение, которому нужно подключиться к серверу с помощью TCP/IP, а затем просто подождать, пока сервер отправит данные, и все, что когда-либо отправляет сервер, должно быть сохранено в файл.

Вот что я сделал:

Заголовочный файл

#ifndef MainH
#define MainH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include <FMX.Controls.Presentation.hpp>
#include <FMX.StdCtrls.hpp>
#include <FMX.Types.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdIOHandler.hpp>
#include <IdIOHandlerStream.hpp>
#include <IdTCPClient.hpp>
#include <IdTCPConnection.hpp>
#include <boost/scoped_ptr.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TIdTCPClient *pTCP;
    TIdIOHandlerStream *IdIOHandlerStream;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall IdIOHandlerStreamGetStreams(TIdIOHandlerStream *ASender, TStream *&VReceiveStream, TStream *&VSendStream);
private:    // User declarations
    boost::scoped_ptr<TFileStream> mFile;
    boost::scoped_ptr<TMemoryStream> mMem;

    void __fastcall StopTcpClick(TObject* Sender);
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

и файл CPP:

include <fmx.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner),
                            mFile(new TFileStream(L"C:\\IbsData.txt", fmCreate | fmOpenReadWrite | fmShareDenyWrite)),
                            mMem(new TMemoryStream())
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    pTCP->Connect();
    Button1->Text = L"Stop";
    Button1->OnClick = StopTcpClick;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::StopTcpClick(TObject* Sender)
{
    pTCP->Disconnect();
    Button1->Text = L"Start";
    Button1->OnClick = Button1Click;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdIOHandlerStreamGetStreams(TIdIOHandlerStream *ASender, TStream *&VReceiveStream, TStream *&VSendStream)
{
    VReceiveStream = mFile.get();
    VSendStream = mMem.get();
}

Я должен отметить, что IdIOHandlerStream был установлен как IOHandler из pTCP.

Проблема в том, что я знаю, что сервер отправляет много данных, но в файл ничего не записывается.

Кто-нибудь знает, почему?


person Sam    schedule 30.11.2018    source источник
comment
Покопавшись еще немного, я также понял, что netstat не показывает никакого активного соединения. Но если я отключу IdIOHandlerStream от TIdTCPClient, то соединение появится в netstat.   -  person Sam    schedule 30.11.2018


Ответы (1)


Вы используете неправильный класс IOHandler.

TIdIOHandlerStream выполняет ввод-вывод с использованием TStream объектов. Обычно он используется для воспроизведения ранее захваченных сеансов в целях отладки без необходимости физического подключения к реальному серверу.

Вместо этого вам нужно использовать TIdIOHandlerStack, который выполняет ввод-вывод с использованием соединения через сокет TCP/IP. Это класс Indy IOHandler по умолчанию, поэтому вам даже не нужно создавать его экземпляр 1, TIdTCPClient::Connect() создаст его для вас внутри, если вы не назначите свой собственный.

1: если вам не требуется более сложное использование, например подключение к серверу через прокси-сервер и т. д., вам понадобится собственный экземпляр, чтобы вы могли настроить его по мере необходимости.

Для того, что вы пытаетесь сделать, позвольте TIdTCPClient использовать TIdIOHandlerStack, а затем вы можете вызвать метод TIdIOHandler::ReadStream() после подключения к серверу. Передайте TFileStream, чтобы он считывался, и установите для параметра AByteCount значение -1, а для параметра AReadUntilDisconnect значение True, чтобы он читал непрерывно, пока соединение сокета не будет закрыто.

Кроме того, как и большинство операций в Indy, ReadStream() блокирует вызывающий поток до завершения, поэтому, чтобы избежать блокировки вашего пользовательского интерфейса, вы должны вызывать ReadStream() в рабочем потоке. Но, если вы не хотите использовать поток, вы можете вместо этого поместить компонент TIdAntiFreeze в свою форму.

person Remy Lebeau    schedule 30.11.2018