Board index » cppbuilder » a IdTCPClient solution

a IdTCPClient solution


2005-07-22 05:25:54 AM
cppbuilder69
This is a client application using IdTCPClient . This application receives a
flow composed of big messages : 10Kbytes each.
I have used some code that was shown in this newsgroup . BUT that was a
server application .
Please , can someone say me if the application-use that I did is correct ?
It seems working ok .
[Rarely I notice errors, They are rare but persistent ..( Connection
refused) It seems to depend of the environment or other problems (?). .]
At the bottom of the mail I report the original suggestions I have read in
this newsgroup (but, for server application)
Perhaps the solution is not the best one as speed or performace : shall I
use streams ? How?
Are there limits to the size of data and the time related throughput ? (
beyond the obvious limits of the network...)
Which is the more simple performant and recommendable approach ?
I had a look at " please help me..socket file transfer" thread , but I
feel a little confused .
More, I must use Indy client ( I have Indy9 installed OK)
Thank you all for any help or opinion
Roberto
the setup includes
TCPClient->Host = iaddress;
TCPClient->Port = iport; // 8023
TCPClient->Connect(0); // Timeout - millisecondi
----------------------------------------------------------------------------
----------
This is called cyclically
ContactThread->socket_read_procedure();
----------------------------------------------------------------------------
----------
void __fastcall TContactThread::socket_read_procedure(void)
{
AnsiString Buffer;
int a,Count,size, size_readable;
int bufsize;
TCPClient->ReadTimeout = 100; //READ_STACK_TIMEOUT;
// $$$ variante newsgroup
try {
if(TCPClient->InputBuffer->Size == 0 )
size_readable =
TCPClient->ReadFromStack(true,IdTimeoutDefault,true);
else
size_readable = TCPClient->InputBuffer->Size;
}
catch(Exception& e){ // Without display
String sbuf = String(e.ClassName()) + " " + e.Message.c_str();
MessageText = sbuf;
// possible error :
EIdReadTimeout . Other errors ??
if (String(e.ClassName()) == "EIdReadTimeout")
return; // lets in waiting read loop
}
if(!size_readable)
return;
if(ser_start <= ser_end)
// circular buffer
size = Min((ser_end + MAX_BUF - ser_start),size_readable);
else
// buffer disposto linearmente :
// spazio libero = semplice
differenza puntatori
size = Min((ser_end - ser_start),size_readable); // al limite,
zero
// si adotta size
in modo da leggere dati solo per lo spazio disponibile
char *buf = new char[size];
TCPClient->ReadBuffer(buf, size); // are possible any errors in this
phase ???
serial_lock=true;
for(a=0; a<size; a++)
{
mychar[ser_end]= buf[a]; // posizione attuale : posizione libera
di inserz.
ser_end++;
char_ready++;
if(ser_end>(MAX_BUF-1)) // eventuale rotazione circolare buffer
ser_end=0;
}
serial_lock=false;
delete[] buf;
return;
----------------------------------------------------------------------------
---------------------------------------------
Server solution provided 14/2/2005
"Jonathan Benedicto" < XXXX@XXXXX.COM >wrote in message
Quote
Eg. If I read data like this :
You should not be looping like that. The OnExecute event is already looped
by TIdTCPServer. Your code should only be processing a single iteration of
that internal loop, not running your own loop at all:
void __fastcall TForm1::IdTCPServer1Connect(TIdPeerThread *AThread)
{
AThread->Connection->ReadTimeout = 1000;
}
void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
{
char buffer[200];
if( AThread->Connection->InputBuffer->Size == 0 )
{
int read = AThread->Connection->ReadFromStack(true,
IdTimeoutDefault, false);
if( read == -1 ) // timeout
return;
}
do
{
int bufsize = min(AThread->Connection->InputBuffer->Size, 200);
AThread->Connection->ReadBuffer(buffer, bufsize);
// use buffer as needed...
}
while( bufsize>0 );
}
Quote
Then the app can only work with 200 bytes chucks of data. Even if the
sending app sent a 100 byte chunk and then a 300 byte chunk.
I would strongly recommend that you prefix your packets with the actual size
of the packets. Then you don't have to limit your buffer size during
reading, and can send small and large packets alike:
void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
{
int size = AThread->Connection->ReadInteger(true);
if( size>0 )
{
char *buffer = new char[size];
AThread->Connection->ReadBuffer(buffer, size);
// use buffer as needed...
delete[] buffer;
}
}
Gambit
 
 

Re:a IdTCPClient solution

"Roberto Zais" < XXXX@XXXXX.COM >wrote in message
Quote
[Rarely I notice errors, They are rare but persistent ..( Connection
refused)
"Connection refused" means that either 1) the server is not running, or 2)
it has too many pending client requeusts and can't accept anymore until it
handles the ones that are already pending.
Quote
TCPClient->ReadTimeout = 100; //READ_STACK_TIMEOUT;
Why are you setting that every time the function is called? You don't need
to. Just set it once before calling Connect(). Or better yet, don't set it
at all. Pass your timeout to ReadFromStack() directly instead of
IdTimeoutDefault.
Quote
if(TCPClient->InputBuffer->Size == 0 )
size_readable =
TCPClient->ReadFromStack(true,IdTimeoutDefault,true);
else
size_readable = TCPClient->InputBuffer->Size;
}
catch(Exception& e){ // Without display
Always catch exception by 'const' references:
catch(const Exception& e){ // Without display
Quote
String sbuf = String(e.ClassName()) + " " + e.Message.c_str();
Don't use c_str() like that:
String sbuf = String(e.ClassName()) + " " + e.Message;
Quote
if (String(e.ClassName()) == "EIdReadTimeout")
return; // lets in waiting read loop
That is not a good way to handle exception testing. If you want to know if
an exception is a particular class type, then you should be using
dynamic_cast instead:
if (dynamic_cast<EIdReadTimeout*>(&e) != NULL )
Better would be to catch the specific exception type directly instead:
catch(const EIdReadTimeout &)
{
return;
}
Better would be to not throw an exception at all. Specify False instead of
True for the ARaiseExceptionOnTimeout parameter of ReadFromStack(). That is
what the earlier code sample was doing.
Quote
if(ser_start <= ser_end)
// circular buffer
size = Min((ser_end + MAX_BUF - ser_start),size_readable);
else
// buffer disposto linearmente
:
// spazio libero = semplice
differenza puntatori
size = Min((ser_end - ser_start),size_readable); // al limite,
zero
How EXACTLY are your packets actually formatted? It doesn't look like you
are taking the advice of the earlier code sample:
"I would strongly recommend that you prefix your packets with the actual
size of the packets. Then you don't have to limit your buffer size during
reading, and can send small and large packets alike"
Quote
TCPClient->ReadBuffer(buf, size);
// are possible any errors in this phase ???
No, if the InputBuffer actually has the specified number of bytes available.
If it doesn't, then it has to call ReadFromStack() again (as well as
CheckForDisconnect()) to read more bytes, and that can potentially throw
exceptions.
Gambit
 

Re:a IdTCPClient solution

First of all , please, I have a question : is TCPClient->Disconnect();
innocuous even if already disconnected , too ??
I didn' t find this information in the help.
Quote
Better would be to not throw an exception at all. Specify False instead
of
True for the ARaiseExceptionOnTimeout parameter of ReadFromStack(). That
is
what the earlier code sample was doing.
so, I have modified the code in this way :
try {
if(TCPClient->InputBuffer->Size == 0 )
size_readable = TCPClient->ReadFromStack(true,100,false);
else
size_readable = TCPClient->InputBuffer->Size;
}
catch(const Exception& e){
String sbuf = String(e.ClassName()) + " " + e.Message;
MessageText = sbuf; // for only eventually debug info
return; // all exception in this phase :
return (wait loop)
// ( there is a global timeout
on the RX operation)
}
if(!size_readable)
return;
else: ...... acquisition of data
Now, I don' t experiment(by debugging) any return by exception: which other
exception are possible , except rxtimeout?
I remember at the beginning, I set the try-catch because it seemed that in
presence of a bad condition ( typically
read timeout) the size returned by TCPClient->ReadFromStack was bad and I
got blockings and bad
results .
What does it happen if I remove the try-catch and set
TCPClient->ReadFromStack(true,100,false); ?
Is size_readable really reliable ?
Quote

How EXACTLY are your packets actually formatted? It doesn't look like you
are taking the advice of the earlier code sample:

"I would strongly recommend that you prefix your packets with the
actual
size of the packets. Then you don't have to limit your buffer size during
reading, and can send small and large packets alike"

Yes, the only reason is that I fear disalignment . But ,really ,there is not
a difference concerning disalignment .
My packets begin with 0x16 and end with 0x4 all other is ASCII char.
When the first byte is lost is lost and the re align procedure is the same .
More, through a TCP conn.
nothing should be lost.
On the whole, is this solution the more common and fast?
Using streams may be better or not ?
Thanks, very much , for your help
Roberto
 

{smallsort}

Re:a IdTCPClient solution

"Roberto Zais" < XXXX@XXXXX.COM >wrote in message
Quote
First of all , please, I have a question : is TCPClient->Disconnect();
innocuous even if already disconnected , too ??
I didn' t find this information in the help.
so, I have modified the code in this way :
You did not pay attention to what the earlier code was doing. Remove the
try..catch, and size_readable will be -1 on timeout so (!size_readable) will
not work. Use (size_readable < 1) instead.
Quote
I remember at the beginning, I set the try-catch because it seemed
that in presence of a bad condition ( typically read timeout) the size
returned by TCPClient->ReadFromStack was bad and I got blockings
and bad results .
That is because you are not checking the size properly.
Quote
Is size_readable really reliable ?
Yes. It returns how many bytes the socket actuall returned and Indy then
buffered for later reading.
Quote
Yes, the only reason is that I fear disalignment . But ,really ,there
is not a difference concerning disalignment .
What are you talking about?
Quote
My packets begin with 0x16 and end with 0x4
That is fine, though including the packet size insode each packet as well
would be better.
With that said, since your packets are already framed, I would suggest not
calling ReadBuffer() until you have first validated that all of the packet
data is actually available. For example (untested):
TCPClient->Host = iaddress;
TCPClient->Port = iport;
TCPClient->ReadTimeout = 100;
TCPClient->Connect(0);
void __fastcall TContactThread::socket_read_procedure(void)
{
int size_readable = TCPClient->ReadFromStack(true, IdTimeoutDefault,
false);
if( size_readable == -1 )
return;
do
{
char *ptr = (char*) TCPClient->InputBuffer->Memory();
int start = MemoryPos("\x16", ptr,
TCPClient->InputBuffer->Size);
if( start < 1 )
{
TCPClient->InputBuffer->Clear();
break;
}
int end = MemoryPos("\x4", ptr+start,
TCPClient->InputBuffer->Size - start);
if( end < 1 )
break;
if( start>1 )
TCPClient->InputBuffer->Remove(start-1);
int size = ((end-start)+1);
char *buf = new char[size];
TCPClient->ReadBuffer(buf, size);
// use buffer as needed...
delete[] buf;
}
while( TCPClient->Connected() );
}
Quote
When the first byte is lost
TCP/IP guarantees data integrity. If data is corrupted, then something is
wrong with the socket in general and it should be reporting errors anyway.
Gambit
 

Re:a IdTCPClient solution

Great! many thanks, Remy .
Quote
>First of all , please, I have a question : is
TCPClient->Disconnect();
>innocuous even if already disconnected , too ??
Have you please an answer about this? Actually there is the risk I close
more times
the connection . Is it allowed ?
thanks
Roberto
 

Re:a IdTCPClient solution

"Roberto Zais" < XXXX@XXXXX.COM >wrote in message
Quote
Have you please an answer about this? Actually there
is the risk I close more times the connection . Is it allowed ?
You could have just looked at Indy's source code for that answer.
Yes, you can call Disconnect() multiple times. If you have multiple threads
involved, though, then I suggest you protect access to the connection so
that threads do not access it at the same time.
Gambit
 

Re:a IdTCPClient solution

Thank you very much for your answer, Remy
Roberto
"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >ha scritto nel messaggio
Quote

"Roberto Zais" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>Have you please an answer about this? Actually there
>is the risk I close more times the connection . Is it allowed ?

You could have just looked at Indy's source code for that answer.

Yes, you can call Disconnect() multiple times. If you have multiple
threads
involved, though, then I suggest you protect access to the connection so
that threads do not access it at the same time.


Gambit