🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.internet.socket
Article #261

Re: Indy10 usage

#261
From: Remy Lebeau (TeamB)
Date: Fri, 07 Mar 2008 00:15
186 lines
9575 bytes
"Mike Ruskai" <BUTthannydI@DONTearthlinkLIKE.netSPAM> wrote in message 
news:nko0t3h85jl7pm57md2te5ttc8b7m6glb7@4ax.com...

> The actual problem is that ReadBytes() is *NOT* calling ExtractToBytes()
> after the socket has been disconnected by the remote, following a
> completed transfer.

Yes, it does.  I have explained to you several times now why it can do so. 
NO EXCEPTION CAN BE RAISED if all of the required bytes have been received 
successfully and put into the InputBuffer.  It does not matter whether or 
not the remote party disconnected after sending the data.  ReadFromSource() 
will not get far enough to see that.

Think about it again.  Let's again assume the server sends 1024 bytes and 
then disconnects.  ReadBytes() is called with 1024 as the requested number 
of bytes.

Whenever ReadFromSource() is called, a single call to recv() is performed, 
passing in a buffer that is RecvBufferSize (32K by default) in size.  If 
even 1 byte is received into that buffer, the buffer is copied into the 
InputBuffer and ReadFromSource() exits without setting the ClosedGracefully 
property.  CheckForDisconnect() then exits without error, and ReadBytes() 
checks the InputBuffer size again, repeating the loop if needed.

Let's say, for the sake of argument, that the first call to ReadFromSource() 
received only 512 bytes.  ReadBytes() would keep looping, calling 
ReadFromSource() again.  Let's say that ReadFromSource() then receives the 
remaining 512 bytes the second time.  It puts them into into the 
InputBuffer, again ClosedGracefully is not set, CheckForDisconnect() again 
exits without error, the loop finally stops, and ExtractBytes() is called.

At no point can EIdConnClosedGracfully be raised by ReadBytes() unless the 
socket returned 0 bytes when ReadFromSource() was expecting at least 1 byte 
to arrive.  The ONLY other ways for the ClosedGracefully property to be set 
to true while ReadBytes() is running is if either 1) you are closing the 
connection from another thread at the same time, or 2) the socket reported a 
WSAESHUTDOWN or Id_WSAECONNABORTED error (in which case the data is lost 
anyway).

If you still think that is not the case in your situation, then please 
provide a stack trace and InputBuffer dump showing otherwise.  Because this 
logic is fundamental to Indy's core design, and has been running the way I 
have described, not the way you have desacribed, for years without problem.

> Asking for more bytes than are available is not a problem, according to
> the dictates of common sense, and the documentation for the function.

That is how the recv() function works.  Not the ReadBytes() method. 
ReadBytes() WILL NOT return less bytes than what you actually asked for. 
That is how ReadBytes() is designed to work.  That is how the documentation 
for ReadBytes() says it works.  Stop trying to treat ReadBytes() the same 
way as recv().  They have very different sementics, on purpose.  Using 
ReadBytes() like recv() is not the correct way to use Indy.

> It will either block until the requested number are available, or return 
> less
> if the socket is closed.

The recv() function does, yes.  The ReadBytes() method does not, no.  It 
raises an exception if the socket is closed before all bytes have been read.

> But it's returning ZERO when the socket is closed, not the total
> actually available.

recv() returns 0 when the disconnect is processed, yes.  At that point, 
there are no more bytes available in the socket to read.  That is when 
ReadBytes() raises the EIdConnClosedGracefully exception.

> But that still doesn't change the fact that ReadBytes() does not
> conform to its documented behavior, as stated above.

Yes, it does, and I have already demonstrated how it does.

> But it's not reading all the data when the socket is disconnected.

Yes, it is.  It keeps reading right up to the point when the socket returns 
0 bytes.  There is no more data available to read after that happens.

> I wrote a simple function to behave more like recv(), so I could
> plug it into a larger routine that loops through received data.

No wonder you are having so many problems.  You are using ReadBytes() in a 
manner that expects it to behave exactly like recv() does - but that is NOT 
how ReadBytes() works!!!  You are not even attempting to use the code I gave 
you earlier.

> The log output above shows conclusively that ReadBytes() does
> not return the last 253 bytes read from the socket.

That is because you asked it to read 7620 bytes, not 253 bytes.  It did 
exactly what it was supposed to do.  It tried to wait until all 7620 bytes 
were available, but after 253 bytes it detected the disconnect and so it 
raised the exception.  That is NORMAL BEHAVIOR for ReadBytes().

In order to use ReadBytes() in the manner you are attempting, you must use 
it more like the following instead:

    int __fastcall DataConstructor::recvData(TIdTCPClient *idDataSock, char 
*data, int dataLen)
    {
        int rlen;
        TIdBytes bdata;

        ofstream dbg("e:\\indyrecv.txt", ios::out | ios::app);

        dbg << "Request: " << dataLen << endl;

        try
        {
            // wait for at least 1 byte to be available for reading
            while (idDataSock->IOHandler->InputBufferIsEmpty())
            {
                idDataSock->IOHandler->CheckForDataOnSource(10);
                idDataSock->IOHandler->CheckForDisconnect(true, true);
            }

            idDataSock->IOHandler->ReadBytes(bdata, 
min(idDataSock->IOHandler->InputBuffer->Size, dataLen), false);

            rlen = bdata.Length;
            if (rlen > 0)
                BytesToRaw(bdata, data, rlen);
        }
        catch (const Exception &)
        {
            rlen = -1;
        }

        dbg << "Total received: " << rlen << endl;

        dbg.close();

        return rlen;
    }

> Let's look at IdIOHandler.ReadBytes() again:
<snip>
> Line 8 is executed, and does nothing, because the last call to
> ReadFromSource() did not set FClosedGracefully.
>
> Line 6 still evaluates to true, so line 7 is executed again.  This time, 
> it
> reads 0 bytes from the socket, and sets FClosedGracefully to True.

FINALLY!!!  You just confirmed what I have been telling you all along.  You 
told ReadBytes() to read too many bytes.  A disconnect was detected before 
all of the requested bytes were received, so an exception was raised.  That 
is what ReadBytes() is supposed to do!!!  You were using ReadBytes() in the 
wrong manner, and then had to write a workaround for your bad code.  If you 
had used ReadBytes() properly in the first place, which I even explained to 
you earlier how to do, you would not have had this problem.

> An exceptionally literal reading of that does suggest that when the socket 
> is
> disconnected, then it was not a "successful completion of the read 
> request".

That is correct.

> In other words, in agreement with what you've been saying, it appears
> to be broken by design.

You code was broken, not ReadByte().  ReadByte() did what it was designed to 
do.

> I stand by my assertion that it is, in fact, a broken design.

Because your mind is still focused on recv()'s behavior.  As I told you 
before, that is NOT how Indy is designed to be used.  Read the documentation 
again.  There is a whole chapter on Indy's design at the very beginning.

> It should not be necessary to know in advance exactly how many bytes to
> read, which is the only way that ReadBytes() can be used by itself to read
> everything from the socket.

That is how Indy is meant to be used in general.  You know what to expect 
ahead of time, so tell it what to get and it gets it from you in a simple, 
blocking operations.  That works fine for 99% of socket programming in 
general.  For the other 1%, which is clearly what you are in, there are 
other mechanisms available to allow you to read arbitrary byte counts more 
dynamically, to which I have already shown you how to do that.  ReadBytes() 
cannot do that by itself, nor is it meant to.


Gambit
Technical Details
Message-ID: <47d088f8@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!news.glorb.com!newsfeeds.ihug.co.nz!ihug.co.nz!newsgroups.borland.com!not-for-mail
References: <ghsqs35tipni65rjlntjpl7hgi6f1bv1g8@4ax.com> <47cd89bb$1@newsgroups.borland.com> <tefrs3lpnfkk0arr2qa3vospikaj3j491h@4ax.com> <47cdc920@newsgroups.borland.com> <tu0ss39uaq8j35if50r88eflpsf5nvs431@4ax.com> <ch7ss31acm13alsqj2s3utjhnbusnks1a9@4ax.com> <47ce339a$1@newsgroups.borland.com> <c65us3ljh7nr89gl6i6sojfkumfuup7qrt@4ax.com> <47cf3c78@newsgroups.borland.com> <n9rus3198r3a36l12l301alsg8o2s7q11i@4ax.com> <47cf8129$1@newsgroups.borland.com> <nko0t3h85jl7pm57md2te5ttc8b7m6glb7@4ax.com>

Show Full Headers
Complete Article Headers
X-Google-Thread: 11508e,ea086d952e8cb041
X-Google-Attributes: gid11508e,public,usenet
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Remy Lebeau \(TeamB\)" <no.spam@no.spam.com>
Newsgroups: borland.public.cppbuilder.internet.socket
References: <ghsqs35tipni65rjlntjpl7hgi6f1bv1g8@4ax.com> <47cd89bb$1@newsgroups.borland.com> <tefrs3lpnfkk0arr2qa3vospikaj3j491h@4ax.com> <47cdc920@newsgroups.borland.com> <tu0ss39uaq8j35if50r88eflpsf5nvs431@4ax.com> <ch7ss31acm13alsqj2s3utjhnbusnks1a9@4ax.com> <47ce339a$1@newsgroups.borland.com> <c65us3ljh7nr89gl6i6sojfkumfuup7qrt@4ax.com> <47cf3c78@newsgroups.borland.com> <n9rus3198r3a36l12l301alsg8o2s7q11i@4ax.com> <47cf8129$1@newsgroups.borland.com> <nko0t3h85jl7pm57md2te5ttc8b7m6glb7@4ax.com>
Subject: Re: Indy10 usage
Date: Thu, 6 Mar 2008 16:15:04 -0800
Lines: 188
Organization: Lebeau Software
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2900.3138
X-RFC2646: Format=Flowed; Original
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198
NNTP-Posting-Host: 207.178.157.90
Message-ID: <47d088f8@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1204848888 207.178.157.90 (6 Mar 2008 16:14:48 -0700)
Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!news.glorb.com!newsfeeds.ihug.co.nz!ihug.co.nz!newsgroups.borland.com!not-for-mail