I have an app that needs to connect to a server using TCP/IP and then just wait for server to send data, and what ever server sends should be saved into a file.
Here is what I did:
The Header file
#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
and the CPP file:
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();
}
I have to note that IdIOHandlerStream
has been set as the IOHandler
of pTCP
.
The problem is, I know server is sending lots of data, but nothing gets written into the file.
Does anyone know why?
TIdIOHandlerStream
performs I/O using TStream
objects. It is typically used for replaying previously captured sessions for debugging purposes, without needing a physical connection to a real server.
You need to use TIdIOHandlerStack
instead, which performs I/O using a TCP/IP socket connection. It is Indy's default IOHandler class, so you don't even need to create an instance of it 1, TIdTCPClient::Connect()
will create one internally for you if you do not assign your own.
1: unless you need more advanced usage, like connecting to a server through a proxy, etc, then you need your own instance so you can configure it as needed.
For what you are attempting, let TIdTCPClient
use TIdIOHandlerStack
and then you can call the TIdIOHandler::ReadStream()
method after connecting to the server. Pass in a TFileStream
for it to read into, and set its AByteCount
parameter to -1 and AReadUntilDisconnect
parameter to True so it will read continuously until the socket connection is closed.
Also, like most operations in Indy, ReadStream()
blocks the calling thread until finished, so to avoid blocking your UI, you should call ReadStream()
in a worker thread. But, if you don't want to use a thread, you can alternately put a TIdAntiFreeze
component on your Form instead.