Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Jan : Re: client/server socket ...

www.cryer.info
Managed Newsgroup Archive

Re: client/server socket ...

Subject:Re: client/server socket ...
Posted by:"Remy Lebeau (TeamB)" (no.spam@no.spam.com)
Date:Thu, 26 Jan 2006 11:49:51

"Luka" <xxsoonic@op.pl> wrote in message
news:43d91884@newsgroups.borland.com...

> I try also use Indy components

Indy does make things simplier.  It does all of the hard work for you that I
showed you how to do manually.

> I got some problems in this case too - can you help me?

That is because your code is still wrong.  See below.

> procedure TForm1.CreateServerClick(Sender: TObject);
> begin
>     idtcpserver.DefaultPort :=strToInt(edit3.text);
>     idtcpserver.Active:=true;
> end;

Make sure that you reset the Port for any items that already exist in the
Bindings property.  Changing the DefaultPort does not effect existing items,
so they will re-bind to their previous Port:

    procedure TForm1.CreateServerClick(Sender: TObject);
    var
        I: Integer;
    begin
        IdTCPServer.DefaultPort := StrToInt(Edit3.Text);
        for I := 0 to IdTCPServer.Bindings.Count-1 do
            IdTCPServer.Bindings[I].Port := IdTCPServer.DefaultPort;
        IdTCPServer.Active := True;
    end;

Or, if you are only interested in 1 Binding that listens on all available IP
addresses:

    procedure TForm1.CreateServerClick(Sender: TObject);
    begin
        IdTCPServer.Bindings.Clear;
        IdTCPServer.DefaultPort := StrToInt(Edit3.Text);
        IdTCPServer.Active := True;
    end;

>
idtcpserver.AThread.connection.WriteBuffer(MyData,SizeOf(MyData),true);

That is not correct.  TIdTCPServer does not have an AThread property.  You
have to use its Threads property instead, which is a TThreadList so you have
to lock it before you can access the connections that are contained in it:

    var
        List: TList;

    List := IdTCPServer.Threads.LockList;
    try
        TIdPeerThread(List[SomeIndex]).Connection.WriteBuffer(MyData,
SizeOf(MyData));
    finally
        IdTCPServer.Threads.UnlockList;
    end;

> procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread);
> var MyData: TMyData;
> begin
>     AThread.connection.ReadBuffer(MyData,SizeOf(MyData));
>     edit1.text := MyData.a;
>     edit4.text := MyData.b;
>     checkbox1.checked := MyData.c;
> end;

That code is not thread-safe.  Indy servers are multi-threaded.  Any event
that provides a TIdPeerThread is executed in the context of that thread.  In
order to access objects in the main thread, you have to use the thread's
Synchronize() method, or better to Indy's TIdSync class:

    type
        TMySync = class(TIdSync)
        protected
            FData: TMyData;
            procedure DoSynchronize; override;
        public
            constructor Create(AThread: TIdThread; const AData: TMyData);
reintroduce;
            class procedure UpdateForm(AThread: TIdThread; const AData:
TMyData);
        end;

    constructor TMySync.Create(AThread: TIdThread; const AData: TMyData);
    begin
        inherited Create(AThread);
        FData := AData;
    end;

    procedure TMySync.DoSynchronize;
    begin
        Form1.Edit1.text := FData.a;
        Form1.Edit4.text := FData.b;
        Form1.CheckBox1.Checked := FData.c;
    end;

    class procedure TMySync.UpdateForm(AThread: TIdThread; const AData:
TMyData);
    begin
        with Create(AThread, AData) do
        try
            Synchronize;
        finally
            Free;
        end;
    end;

Then you can do the following:

    procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread);
    var
        MyData: TMyData;
    begin
        AThread.Connection.ReadBuffer(MyData,SizeOf(MyData));
        TMySync.UpdateForm(AThread, MyData);
    end;

> procedure TForm1.IdTCPClientConnected(Sender: TObject);
> var MyData: tMyData;
> begin
>     idtcpclient.ReadBuffer(MyData,SizeOf(MyData));
>     edit1.text := MyData.a;
>     edit2.text := MyData.b;
>     checkbox1.checked := MyData.c;
> end;

Unless your server code writes data to the client, then you are going to be
locking up your client application completely.  Indy uses an infinite
reading timeout by default, and the OnConnected event is triggered by
Connect() which is being called in a button OnClick handler.  When
ReadBuffer() blocks the OnClick handler, the rest of the applicaton is
blocked as well.

If your client does not know when the server is going to be sending data,
then you should be using a timer or thread to do the reading.  In the case
of a timer, use the client's Readable() method with a small timeout to know
whether any data is actually available before reading it.  In the case of a
thread, is it ok to let the reading block the thread until data actually
arrives.

That way, the application is not being blocked unnecesarily.

> What I'm trying to do is: when I press Send Button on server it will send
> MyData to a client and the client would receive (show) it automatically,
> when I press Send button on client form it will send Mydata to the server
> and also it will show automatically when it receives. If there will be
more
> clients connected to the server I want be able to choose a client.

The code I have shown you above will handle all of those situations.


Gambit

Replies:

In response to:

www.cryer.info
Managed Newsgroup Archive