🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

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

Re: Detecting a client disconnect

#4839
From: Remy Lebeau (TeamB)
Date: Wed, 24 Nov 2004 22:41
166 lines
7135 bytes
"Paul Machiaverna" <paul.machiaverna@i-stat.com> wrote in message
news:41a4fb02$1@newsgroups.borland.com...

> The network communuication code in the application is from the
> days from WinShoes with minor modifications to work with Indy.

WinShoes is a very different library from Indy.  You really need to update
to true Indy logic, not WnShoe-adapted logic.

> We have serial (RS-232) devices that are connected to RS-232 to
> TCP/IP-Ethernet converters (device servers).

An alternative is to write a TIdIOHandler descendant class that reads from
and writes to the serial port directly.  Then Indy can operate on the ports
without using converters.

> One example is a device that communicates binary data  at 19.2 Kbps with
> NO protocol.  It's a raw data dump.  This data varies in size, but always
starts
> and ends with known strings, "BEGIN" and "END".  The device servers
packetize
> this data at whatever algorithm they choose.  A connection to the server
(INDY
> application) whenever data is detected on the serial port.

Ok, now you are confusing me about who is who in this situation.  Please use
more descriptive names then "device" and "server".  Your description
suggestions the "device" is the "Server" is that right?  Why is the data not
using any protocol?  Even a raw dump send by the TIdTCPServer should use
some protocol to help the client manage the data reading easier.  You could
use a protocol like this:

    void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
    {
        if( first time )
        {
            AThread->Connection->WriteInteger(5);
            AThread->Connection->Write("BEGIN");
            first time = false;
        }

        // read data from serial port as needed
        if( data available )
        {
            AThread->Connection->WriteInteger(size of raw data);
            AThread->Connection->WriteBuffer(raw data, size of raw data);
        }

        if( no more data available )
        {
            AThread->Connection->WriteInteger(3);
            AThread->Connection->Write("END");
        }
    }

That way, the client can read an integer first, which tells how many
remaining bytes to read for that particular packet.  If the packet data is
"BEGIN" then start processing future raw data.  If the packet data is "END"
then stop processing future raw data.  Everything else is raw data.

    bool processraw = false;
    while( connected )
    {
        read an integer
        read specified number of bytes

        if( bytes are "BEGIN" )
            processraw = true;

        else if( bytes are "END" )
            processraw = false;

        else if( processraw )
            process bytes as needed
    }

So you see, even with a small protocol, you still preserve your raw dump
while providing easier reading.  No more buffering every single byte.  No
more state machines.

> From the server perspective, a client connects and data is read from
> the socket.  Then this data is transferred to a receive buffer that is
large
> enough to hold the entire (maximum) number of bytes.

That does not sound like a very good design to me.  Especially since Indy
already maintains its own receive buffer anyway.  You don't need your own
buffering.

> As data is transferred to this buffer each byte is fed through a state
machine.
> This detects the "BEGIN" and "END", which are triggers for sending meta
data back towards the device.

So, are you saing that the "device" is actually the client and the
TIdTCPServer will be receiving the "BEGIN and "END" strings rather than
issuing them to the client?  If that is the case, then the situation is
still easy to manage:

    void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
    {
        if( AThread->Connection->ReadFromStack(true, 125) != -1 )
        {
            int index;

            if( firsttime )
            {
                index = find index of "BEGIN" in
AThread->Connection->InputBuffer;
                if( index == -1 )
                    return;

                AThread->Connection->InputBuffer->Remove(index+5);
                first time = false;
            }

            index = find index of "END" in AThread->Connection->InputBuffer;
            if( index == -1 )
                return;

            // process AThread->Connection->InputBuffer->Memory
            // up to index number of bytes as needed...

            AThread->Connection->InputBuffer->Remove(index+3);
            first time = true;
        }

        if( data needs to be transmitted )
            AThread->Connection->WriteBuffer(data to send, size of data to
send);
    }

> There is yet another layer of complexity to this.  When the "END" is
received,
> data processing is performed and there are some situations where a
secondary
> transmission session is started by the server to update parameters in the
device.
> This transmission is protocol driven using an implementation of ASTM.

Indy works very well with protocols.

> We all that said, changing over to a different scheme is going to be quite
a bit
> of work.  Note that the above scheme has been working very well (extremely
> stable) for over 5 years.  All I'm trying to add is the ability to detect
a client
> disconnect reliably.

To do that, you first need to use Indy the way it was designed to be used.
That means NOT calling Readable() and Recv() directly.  Use the reading
methods of TIdTCPConnection instead of TIdSocketHandle directly.
TIdTCPConnection performs all of the necessary error handling and such to
detect and report disconnects.

> Supporting the old transmissions is a pain at best in these days of
packetized
> protocols. We (my company) had called in a few different consultants to
talk
> about how to support our legacy devices in network communications.

I just described above how you can do it.


Gambit
Technical Details
Message-ID: <41a50da0$1@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!news.agarik.com!npeer.de.kpn-eurorings.net!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail
References: <41a3a222$1@newsgroups.borland.com> <41a3a429@newsgroups.borland.com> <41a4aed9$3@newsgroups.borland.com> <41a4d9d0$1@newsgroups.borland.com> <41a4e2b4$1@newsgroups.borland.com> <41a4f0e2@newsgroups.borland.com> <41a4fb02$1@newsgroups.borland.com>

Show Full Headers
Complete Article Headers
X-Google-Thread: 11508e,2bbc0c2eefeb0788
X-Google-Attributes: gid11508e,public
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Remy Lebeau \(TeamB\)" <no.spam@no.spam.com>
Newsgroups: borland.public.cppbuilder.internet.socket
References: <41a3a222$1@newsgroups.borland.com> <41a3a429@newsgroups.borland.com> <41a4aed9$3@newsgroups.borland.com> <41a4d9d0$1@newsgroups.borland.com> <41a4e2b4$1@newsgroups.borland.com> <41a4f0e2@newsgroups.borland.com> <41a4fb02$1@newsgroups.borland.com>
Subject: Re: Detecting a client disconnect
Date: Wed, 24 Nov 2004 14:41:38 -0800
Lines: 168
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2800.1409
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409
NNTP-Posting-Host: 68.69.17.41
Message-ID: <41a50da0$1@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1101335968 68.69.17.41 (24 Nov 2004 14:39:28 -0700)
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!news.agarik.com!npeer.de.kpn-eurorings.net!newsfeed00.sul.t-online.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail