Link to home
Start Free TrialLog in
Avatar of epagos
epagosFlag for Argentina

asked on

How can i make a multithreaded app with TTcpServer on delphi?

Hello guys,

I have made an application on delphi using TTCPServer and TTCPClient without using vlc (just plain code).
I want to make it multithreaded but i dont have much idea how to do it. Can anyone show me an example of how to do  it?

NOTE: Do not tell me about Indy or any of those components, i know they are exellent and they already comes with multithreading but they are much big in size that ttcpserver component and i need the exe file to be SMALL.

Thanks in advance,

Elias
Avatar of 2266180
2266180
Flag of United States of America image

1st: no matter that you use the IDE or plain code, if you use VCL components, then you are using VCL :)

2nd: I don't remember seeing such an example, but the idea behind it is really simple.
- when a client connects (in the onaccept), you create a new thread. that thread will have a member that holds the connection (so it can keep reading from it). you should put that thread in a list (TObjectLsit for example) os that you can manage the connections.
- when a client disconnects, search for that thread in the list (you know the disconnecting client and each threads connection) and remove it from list and destroy it
the server can expose an event, like onexecute, similar to indy and the threads will call that event when they have some data to be read.

let me know if that does not make sense for you
Avatar of epagos

ASKER

hey ciuly,

Yes it makes sense, its basically what im trying to do. For what i can read on sockets.pas (TTcpServer and TTcpClient unit) it automatically destroy the thread after the client disconnect so i should not worry about that.

I have atached the code of the app. Right now it works like a proxy server, its the easiest way for testing the multithreading (i still didnt add the real functions for it).

I have 2 problems, the first one is the multithreading issue, and the second one is that sometimes it wont take new incoming connections, wich i think is because of the first issue.

Any help is welcome :)

Thanks,
Elias


program eclipse;
 
{$APPTYPE CONSOLE}
 
uses
  sysutils,
   sockets in 'sockets.pas';
 
 
 
type
      Server = class(TTcpServer)
   private
 
   public
       Procedure Start;
       procedure FOnAccept(Sender: TObject; ClientSocket: TCustomIpClient);
       Procedure FGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
  end;
 
  type
      Client = class(TTcpClient)
  end;
 
 
  type             
  SocketClient = class(TClientSocketThread)
  private              
    { Private declarations }
  public
    procedure Execute(ClientSocket: TCustomIpClient);
  end;
 
 
 
  var  Server1: Server;
  Client1: array[0..100] of Client;  // the program will just use 1 client, initially i made an array of 100 clients to test.
  clients: integer;
  //ServerThread: SocketClient;
 
 
procedure SocketClient.Execute (ClientSocket: TCustomIpClient);
begin
   writeln('Thread Executed'); // This event is never executed, i dont know why.
end;
 
Procedure Server.FGetThread(Sender: TObject; var ClientSocketThread: TClientSocketThread);
begin
  writeln('Creating Thread');
  ClientSocketThread := SocketClient.Create(Server1.ServerSocketThread);
end;
 
Procedure Server.Start();
Begin
 
 
    server1 := Server.Create(nil);
    Server1.Active := False;
    Server1.LocalPort := '1759';
    Server1.Localhost := Server1.LocalHostName;
    Server1.BlockMode := bmThreadBlocking;
    Server1.OnAccept := Server1.FonAccept;
    Server1.OnGetThread := Server1.FGetThread;
 
    Server1.Active := True;
 
    repeat
      sleep(500);
    until 1=2; // I make sure that the server wont stop
 
End;
 
procedure Server.FOnAccept(Sender: TObject; ClientSocket: TCustomIpClient);
var
  s,x,y: string;
  Rbuf: array[0..32768] of char;
  RCount, counter, a: integer;
 
begin
 
    s:=ClientSocket.Receiveln();
    Repeat
 
         if(pos('Host:',s) > 0) then
          begin
            y := StringReplace(s, 'Host:','', [rfReplaceAll, rfIgnoreCase	]);
            y := StringReplace(y, ' ','', [rfReplaceAll, rfIgnoreCase	]);
          end;
         if(pos('Proxy-Connection:',s) > 0) then
         begin
           s := StringReplace(s, 'Proxy-Connection:','Connection:', [rfReplaceAll]);
         end;
 
         x:= x + s;
 
         if(s<>'') then begin x:= x + #13 + #10; end;
         s:=ClientSocket.Receiveln();
    Until s='';
 
    for a := 1 to clients do
    begin
      if Client1[a].Connected = False then counter := a;
    end;
 
    Client1[counter].RemoteHost := y;
 
 
     while Client1[counter].Connected = True do begin
         Client1[counter].WaitForData(100);
     end;
 
     try
       Client1[counter].Active := True;
       if Client1[counter].Connect then
       begin
         Client1[counter].Sendln(x);
         Client1[counter].Sendln('');
      end;
     finally
     end;
      RCount := Client1[counter].ReceiveBuf(Rbuf, 1024);
      ClientSocket.SendBuf(Rbuf,RCount);
      sleep(100);
      while Client1[counter].WaitforData(1000) AND Client1[counter].connected do
      begin
           RCount := Client1[counter].ReceiveBuf(Rbuf, 4092);
           if ClientSocket.Connected = True then
              ClientSocket.SendBuf(Rbuf,RCount)
           else  begin client1[counter].Disconnect;  break; end;
 
           sleep(100);
      end;
 
     Client1[counter].Disconnect;
     writeln('Client Disconecting');
  end;
 
// CLIENT START
 
var cc : integer;
begin
clients := 100;
for cc := 0 to clients do
  begin
  Client1[cc] := Client.Create(nil);
  Client1[cc].BlockMode := bmBlocking;
  Client1[cc].RemotePort := '80';
  end;
Server1.start();
end.

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of 2266180
2266180
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
sprry about the madexcept stuff. you can remove the first mad* units :)
Avatar of epagos

ASKER

Thanks a LOT for all those info. As u can see im a real noob on delphi :)

I will check your code and let u know if i have any other question. About all those clients they are not for connecting to the server, initially i wanted to do a very simple proxy server, so when the server received a connections it will order the clients to get the requested url.  (thats the correct method?)

Elias
not exactly. a proxy is somethign that intermediates.

so if the client says:
GET something
the proxy will get that something and then send it back to the client.

soi, either you don't want a "proxy" but something else, or you miss-explained the behaviour.
Avatar of epagos

ASKER

thats what the app is doing, when the server1.accept is executed it will get the header of the request and then it will GET that website through the client components and then it will return it through clientsocket on Server1.Accept
Avatar of epagos

ASKER

oh i forgot...

Im trying to connect to this app from other diferent pcs on diferent networks... sometimes it works and it receives the remote connection and sometimes it just do not receive it. Do u see any problem on the code wich can be causing that?

Just from my pc i connect from my own machine i can connect about 80-90% of the times.
I see. well, I'm not sure how to test the behaviour you are saying. what are you using? a browser is ok?

as I said, you will have problems with not synchronizing the resources accessed by many threads. for one axample: the clients array. plus that warning I mentioned. etc.

so, try to put all my suggestions into practice in your code and I'll continue on it tomorrow. (I'm off to bed now)