HOME - - - - - Delphi Tutorials TOC - - - - - - - - - - - - Other material for programmers
    Delicious Bookmark this on Delicious   Recommend to StumbleUpon

Delphi: Playing with a basic demo from the OverByte ICS

to learn about some of the first principles of TCP/IP

This has good information, and a search button at the bottom of the page

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

More information about the source and format of these pages is available.


This tutorial starts with one of the demos in Francois Piette's marvelous, and free!, Internet Component Suite, or "ICS".

When you have installed this on your computer it makes doing TCP/IP programming relatively easy in Delphi, or in Borland C++ Builder. As easy as TCP/IP work can be, I would guess. Sadly, no TCP/IP work is ever going to be "easy".

This tutorial follows on the heels of several others. If you haven't read them already, you might want to look at my pages about what the ICS is, how to install it, and the question of writing non-blocking, asynchronous code. (All of those open in their own pages, to make getting back here easy for you.)

Onward?

Apologies to those of you who had already looked at all the resources I've just mentioned.

We're going to start with a demo which comes with the ICS, and "play" with it just a little.

The demo is called "OverByteICSHttpTst", and you will find it in the "Internet" folder of your ICS installation.

I took copies of all of the "OverByteICSHttpTst" files (several, different extensions) and the "OverByteICSHttpTst1" files, and moved them to a new folder. If I "messed something up", I didn't want to lose access to the original demos.

The OverByteICSHttpTst demo is capable of more things than I can begin to discuss. To be honest, I don't really know much about TCP/IP... but I know enough to discuss and work with some of what is on offer in OverByteICSHttpTst.

If you just load the demo as it is supplied into your Delphi, you may get a message about "Error reading IcsLogger1.LogFile..." Happily, one option you are offered is "Ignore". I did this, and didn't, as far as I know, suffer any adverse results.

While we're on the subject of things I ignored, I'll mention the other.

If you look in the .dpr file for OverByteICSHttpTst (I'll tell you how, in a moment) you will see....

{$R '..\Vc32\OverbyteIcsXpManifest.res' '..\Vc32\OverbyteIcsXpManifest.rc'}
{$R '..\Vc32\OverbyteIcsCommonVersion.res' '..\Vc32\OverbyteIcsCommonVersion.rc'}

I've played with these files and these lines quite a bit. For what we are going to do here, their presence, availability or absence seems to make no difference! (.res files are often about icons and other things, the .rc files can be connected with making certain things editable. See the $R directive information in the Delphi help files, and note that {$R} with a plus or minus sign has a quite different meaning.)

You can either use a text editor to view the contents of OverByteICSHttpTst.dpr, or you can use the "View|Units" option from the Delphi menu, and ask for "OverByteICSHttpTst". (The Delphi interface doesn't give you the extension.)

Moving on...

We've talked about things we can ignore... let's look at some things we want to pay attention to!

Load your "scratch" copy of OverByteICSHttpTst into your ICS-equipped Delphi, and run it.

A window should open with lots of edit boxes, captions, buttons, and two memos. Don't worry... we will be leaving most of them alone!

Click the "Get" button at the upper right. If your firewall will allow it, you should go off to a webpage, and collect the underlying HTML of that page. Once fetched, it will appear in the memo at the bottom of the page.

As supplied, the demo will try to fetch the HTML at....

http://www.overbyte.be

(The page the application goes off to is specified by the edit box at the upper right, the one labeled "URL".

And once again, we hit a diversion.

The application is quite "clever" in remembering things from the last time you ran it. Move the window, resize it, etc, and your changes will persist. Change the URL the application is trying to access, and that will persist too!

Just before we go back to the TCP/IP issues, I just want to say a word about where OverByteICSHttpTst saves this information.

Not in the registry, thankfully.

It saves the information in a simple text file called "OverbyteIcsHttpTst.ini", which is in....

C:\Documents and Settings\{your user name}\Local Settings\Application Data\ICS

This is well and good, most of the time, if you don't mind "protecting" your users from things that might confuse them. It is very useful if you want different users (i.e. people logged in under different user names) to have individualized experiences.

The critical line of code is...

FIniFileName := GetIcsIniFileName;

If you change that to....

FIniFileName := 'MyPlayIni.txt';

... then the ini file for the application becomes MyPlayIni.txt, and it will be stored in whatever folder the application's .exe is in. If you make that line....

FIniFileName :='C:\MyStuff\MyPlayIni.txt';

... then the ini file will go in a folder called "MyStuff", in the root of the C: drive. You will have to create the folder by hand before running the application.

(All of these options have pros and cons which could be debated for hours... in another time and place!)

Back to TCP/IP issues

So! You have by now, I hope, got a working scratch copy of OverByteICSHttpTst up, and running as it ran when it "left the factory"?

We're now going to tinker with just a fraction of it, in hopes of learning a little bit about what is going on.

We're going to "clone" a version... full of scraps from the original that we won't be using, just ignoring... a version that will check and re-check a particular website, automatically.

First steps...

Our clone will be automating the "Get" function, using a timer component to achieve the read and re-read. We're also going to move the 5th line of whatever HTML is read to a label on the form, just too prove that we can. All sorts of things become possible once you have these basic things working.

If you go to the form view of the application, while in the design mode (application not running) and double click on the "Get" button, you will be switched to the window showing the code behind the form, with the OnClick handler for the "Get" button. It is as follows....

procedure THttpTestForm.GetButtonClick(Sender: TObject);
var
    I       : Integer;
    DataIn  : TStream;
begin
    DisplayMemo.Clear;
    DocumentMemo.Clear;
    SetButtonState(FALSE);

    try
        HttpCli1.URL            := URLEdit.Text;
        HttpCli1.Proxy          := ProxyHostEdit.Text;
        HttpCli1.ProxyPort      := ProxyPortEdit.Text;
        HttpCli1.AcceptLanguage := 'en, fr';
        HttpCli1.Connection     := 'Keep-Alive';
        HttpCli1.RequestVer     := '1.' + IntToStr(HttpVersionComboBox.ItemIndex);
        HttpCli1.RcvdStream := nil;
        if DateTimeEdit.Text <> '' then
            HttpCli1.ModifiedSince := StrToDateTime(DateTimeEdit.Text)
        else
            HttpCli1.ModifiedSince := 0;

        if HttpCli1.Proxy <> '' then
            Display('Using proxy ''' + HttpCli1.Proxy + ':' +
                    HttpCli1.ProxyPort + '''')
        else
            Display('Not using proxy');

        try
            HttpCli1.Get;
        except
            Display('GET Failed !');
            Display('StatusCode   = ' + IntToStr(HttpCli1.StatusCode));
            Display('ReasonPhrase = ' + HttpCli1.ReasonPhrase);
            HttpCli1DocEnd(nil);  { This will close the file }
            Exit;
        end;

        Display('StatusCode = ' + IntToStr(HttpCli1.StatusCode));

        if DisplayHeaderCheckBox.Checked then
            for I := 0 to HttpCli1.RcvdHeader.Count - 1 do
                Display('hdr>' + HttpCli1.RcvdHeader.Strings[I]);

        if Length(DocFileName) = 0 then begin
            DocumentMemo.Lines.Add('*** NO DOCUMENT FILE NAME ***');
        end
        else begin
            DataIn := TFileStream.Create(DocFileName, fmOpenRead);
            try
                if Copy(HttpCli1.ContentType, 1, 5) = 'text/' then
                    DocumentMemo.Lines.LoadFromStream(DataIn)
                else begin
                    DocumentMemo.Lines.Add('Content type is ' +
                                           HttpCli1.ContentType);
                    DocumentMemo.Lines.Add('Document stored in ''' +
                                           DocFileName +
                                           ''' Size=' + IntToStr(DataIn.Size));
                end;
            finally
                DataIn.Free;
            end;
        end;
    finally
        SetButtonState(TRUE);
    end;
end;

We'll be working with a copy of that in a moment. For now, please bear with me for a few paragraphs/ steps?

Put a new button on the form, call it buTKBGet. ("TKB" being my initials.)

Double click on the button, and make it's OnClick handler be....

procedure THttpTestForm.buTKBGetClick(Sender: TObject);
begin
DoATKBGet;
end;

Don't try to run the application yet... it won't run.

Up at the top of the program, just before the word "private", add the line you see in context below....

    procedure ClearButtonClick(Sender: TObject);
    procedure PutButtonClick(Sender: TObject);
    procedure buTKBGetClick(Sender: TObject);
    procedure DoATKBGet;
  private
    Initialized  : Boolean;
    DocFileName  : String;

... and at the bottom of the code, just before the "end." at the end of it all, add...

procedure THttpTestForm.DoATKBGet;
begin
Showmessage('we are making progress');
end;

One more thing... sigh... up at the top of the code, add "Dialogs" to the "Uses" clause, i.e. make it....

uses
  Dialogs, Windows, Messages,...

Now, you can run the program, and click buTKBGet! It won't do much yet, but it should put a message on the screen.

Deep breath...

Remember the "GetButtonClick" code, from the unmodified demo, which we looked at briefly earlier? Copy that code into the DoATKBGet procedure which we just created. Make it, in its entirety....

procedure THttpTestForm.DoATKBGet;
var
    I       : Integer;
    DataIn  : TStream;
begin
    DisplayMemo.Clear;
    DocumentMemo.Clear;
    SetButtonState(FALSE);

    try
        HttpCli1.URL            := URLEdit.Text;
        HttpCli1.Proxy          := ProxyHostEdit.Text;
        HttpCli1.ProxyPort      := ProxyPortEdit.Text;
        HttpCli1.AcceptLanguage := 'en, fr';
        HttpCli1.Connection     := 'Keep-Alive';
        HttpCli1.RequestVer     := '1.' + IntToStr(HttpVersionComboBox.ItemIndex);
        HttpCli1.RcvdStream := nil;
        if DateTimeEdit.Text <> '' then
            HttpCli1.ModifiedSince := StrToDateTime(DateTimeEdit.Text)
        else
            HttpCli1.ModifiedSince := 0;

        if HttpCli1.Proxy <> '' then
            Display('Using proxy ''' + HttpCli1.Proxy + ':' +
                    HttpCli1.ProxyPort + '''')
        else
            Display('Not using proxy');

        try
            HttpCli1.Get;
        except
            Display('GET Failed !');
            Display('StatusCode   = ' + IntToStr(HttpCli1.StatusCode));
            Display('ReasonPhrase = ' + HttpCli1.ReasonPhrase);
            HttpCli1DocEnd(nil);  { This will close the file }
            Exit;
        end;

        Display('StatusCode = ' + IntToStr(HttpCli1.StatusCode));

        if DisplayHeaderCheckBox.Checked then
            for I := 0 to HttpCli1.RcvdHeader.Count - 1 do
                Display('hdr>' + HttpCli1.RcvdHeader.Strings[I]);

        if Length(DocFileName) = 0 then begin
            DocumentMemo.Lines.Add('*** NO DOCUMENT FILE NAME ***');
        end
        else begin
            DataIn := TFileStream.Create(DocFileName, fmOpenRead);
            try
                if Copy(HttpCli1.ContentType, 1, 5) = 'text/' then
                    DocumentMemo.Lines.LoadFromStream(DataIn)
                else begin
                    DocumentMemo.Lines.Add('Content type is ' +
                                           HttpCli1.ContentType);
                    DocumentMemo.Lines.Add('Document stored in ''' +
                                           DocFileName +
                                           ''' Size=' + IntToStr(DataIn.Size));
                end;
            finally
                DataIn.Free;
            end;
        end;
    finally
        SetButtonState(TRUE);
    end;
end;

... and run the application again. Click buTKBGet. The result should be the same as if we click the original "Get" button.

Not much progress, you might cry! BUT! Notice this: We've "hived off" "the bit we need". The bit we need to have happen repeatedly, via the timer.

Add a timer to the form. Set it's interval to 5000.

Double click on the timer, and fill in the resulting skeleton thus...

procedure THttpTestForm.Timer1Timer(Sender: TObject);
begin
DoATKBGet;
end;

Now do you see the "cunning plan" that I was working to? Run the application again, but don't, this time, click any buttons. Every 5 seconds you should see the memos at the bottom of the application's window go blank briefly, and then refill. (Assuming you've put a valid address in the "ULR" edit box.) (The first filling of the memos won't happen until the first 5 seconds have ticked by.)

A very good start!

We've made a very good start on our declared objective!

You might think we were nearly done. "All" we need is a label, and a line of code to pick up the 5th line of the memo, and put it in our label, whenever the memo is refilled.

See the "little problem"?

How do we know WHEN to pick up the 5th line? We'll come to that.

For now, start by putting a label on the form. Call it laTKBFrmHTML.

And, for now, turn off the automatic (every 5 seconds) fetching of the html by setting the timer's enabled property to false. (We'll use the buTKBGet button to see if we've got things right.)

You've been good, so I'm going to tell you where to put the...

laTKBFrmHTML.caption:=DocumentMemo.lines[4];

... line, which is the vital last step to finishing what we set out to do.... but you have to promise to read on, and learn WHY it goes there. Okay? Right....

Insert the new line as follows....

finally
    DataIn.Free;
    laTKBFrmHTML.caption:=DocumentMemo.lines[4];
 end;

... down near the end of procedure THttpTestForm.buTKBGetClick(Sender: TObject); Be careful that you don't accidentally put it in the corresponding position within the procedure from which buTKBGetClick was cloned!!

Why did that work? What was hard about it?

What was so hard about that?

Well, you might have put the in a different, "obvious" place if left to your own devices. Why not put it just after the except handler after the "HttpCli1.Get;". That isn't a completely "dumb" idea... it "should" work... except for one little thing.

Immediately, and for a little while (in computer terms) after the HttpCli1.Get has been done, the reply has not yet come back from the remote server. The memo hasn't been filled. So if you fetch the 5th line with code just after the HttpCli1.Get, you won't fetch what you would expect.

How does putting the "laTKBFrmHTML.caption:=DocumentMemo.lines[4];" where I've told you to work? How come we never fetch "too soon" if we put it there.

To be honest... don't FULLY understand all of what I'm about to say myself(!) But there will be a lot of truth in it!!

After we invoke HttpCli1's "Get" method, certain things are set in motion. Behind the scenes, inside the HttpCli1 component, a request is sent off to the remote server. As the document from that begins to form, a "DocBegin" event occurs, and our code has an OnDocBegin handler you can examine. There's also a DocEnd event, with handler, and a RequestDone event and handler.

They, and a TFileStream object called "DataIn" are all working together. I was lucky enough to spot the "DataIn.Free" statement in the code, and said to myself "That's the place to put my "laTKBFrmHTML.caption:=DocumentMemo.lines[4];" statement! The program couldn't be destroying ("freeing") the DataIn object before the transfer of the data to the memo was complete. Whew!

Apologies for not doing more to explain the details of how those elements work together. I'd rather disappoint you than say things that are wrong, and get in the way of your understanding. I hope the underlying "problem" of waiting until the "fetch" is complete is understood... it is the "magic" in this part of the many things that OverByteHttpTst can do!

Enough for now?

That, my friends, is that... for now. I hope you will be relieved, rather than disappointed. And thank you for struggling through to this point. Go back to the code, and see if you can sort out all the details of what is happening in the various event handlers, and with the TFileStream object. Everything I am fit to judge in the OverByte demo code is very neatly done... we can learn things from studying these demos! And not just how to get TCP/IP stuff done.


            powered by FreeFind
  Site search Web search
Site Map    What's New    Search This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"

Click here if you're feeling kind! (Promotes my site via "Top100Borland")


If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service. They offer things for the beginner and the corporation.www.1and1.com icon

Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.

Link to Tutorials main page
How to email or write this page's editor, Tom Boyd


Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....