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?
You are using the wrong IOHandler class.
TIdIOHandlerStream
performs I/O usingTStream
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
useTIdIOHandlerStack
and then you can call theTIdIOHandler::ReadStream()
method after connecting to the server. Pass in aTFileStream
for it to read into, and set itsAByteCount
parameter to -1 andAReadUntilDisconnect
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 callReadStream()
in a worker thread. But, if you don't want to use a thread, you can alternately put aTIdAntiFreeze
component on your Form instead.