使用 TIdIOHandlerStream 和 TIdTCPClient 读取数据流
Reading stream of Data using TIdIOHandlerStream and 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
已设置为 pTCP
的 IOHandler
。
问题是,我知道服务器正在发送大量数据,但没有任何内容写入文件。
有人知道为什么吗?
您使用了错误的 IOHandler class。
TIdIOHandlerStream
使用 TStream
对象执行 I/O。它通常用于重播以前捕获的会话以进行调试,而不需要与真实服务器的物理连接。
您需要改用 TIdIOHandlerStack
,它使用 TCP/IP 套接字连接执行 I/O。它是 Indy 的默认 IOHandler class,所以你甚至不需要创建它的实例 1,如果你这样做,TIdTCPClient::Connect()
会在内部为你创建一个不分配你自己的。
1:除非你需要更高级的用法,比如通过代理连接到服务器等,否则你需要自己的实例,这样你就可以根据需要进行配置。
对于您的尝试,让TIdTCPClient
使用TIdIOHandlerStack
,然后您可以在连接到服务器后调用TIdIOHandler::ReadStream()
方法。传入一个TFileStream
让它读入,并将其AByteCount
参数设置为-1,AReadUntilDisconnect
参数设置为True,这样它就会连续读取,直到套接字连接关闭。
此外,与 Indy 中的大多数操作一样,ReadStream()
会阻塞调用线程直到完成,因此为避免阻塞您的 UI,您应该在工作线程中调用 ReadStream()
。但是,如果您不想使用线程,您也可以在表单上放置一个 TIdAntiFreeze
组件。
我有一个应用程序需要使用 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
已设置为 pTCP
的 IOHandler
。
问题是,我知道服务器正在发送大量数据,但没有任何内容写入文件。
有人知道为什么吗?
您使用了错误的 IOHandler class。
TIdIOHandlerStream
使用 TStream
对象执行 I/O。它通常用于重播以前捕获的会话以进行调试,而不需要与真实服务器的物理连接。
您需要改用 TIdIOHandlerStack
,它使用 TCP/IP 套接字连接执行 I/O。它是 Indy 的默认 IOHandler class,所以你甚至不需要创建它的实例 1,如果你这样做,TIdTCPClient::Connect()
会在内部为你创建一个不分配你自己的。
1:除非你需要更高级的用法,比如通过代理连接到服务器等,否则你需要自己的实例,这样你就可以根据需要进行配置。
对于您的尝试,让TIdTCPClient
使用TIdIOHandlerStack
,然后您可以在连接到服务器后调用TIdIOHandler::ReadStream()
方法。传入一个TFileStream
让它读入,并将其AByteCount
参数设置为-1,AReadUntilDisconnect
参数设置为True,这样它就会连续读取,直到套接字连接关闭。
此外,与 Indy 中的大多数操作一样,ReadStream()
会阻塞调用线程直到完成,因此为避免阻塞您的 UI,您应该在工作线程中调用 ReadStream()
。但是,如果您不想使用线程,您也可以在表单上放置一个 TIdAntiFreeze
组件。