Recent

Author Topic: can i make server?  (Read 69002 times)

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #60 on: October 20, 2017, 06:02:30 pm »
Is the server lagging already before pressing the button?
Because that shouldn't happen.

If it's  lagging after pressing the button it's because of the reason I already mentioned.

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #61 on: October 21, 2017, 02:29:32 am »
will it be better if i put 1 instead of 100 then?
Code: Pascal  [Select][+][-]
  1. if ListenerSocket.canread(100)

with the new code you gave me, it's not lagging anymore after i press the button but if send message from client like 5 times? the serer get laggy and not responding again but if i close the client program it works

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #62 on: October 21, 2017, 06:57:14 am »
What code are you using now for client and server?

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #63 on: October 21, 2017, 07:46:51 am »
client
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, blcksock;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Edit1: TEdit;
  17.     Edit2: TEdit;
  18.     Edit3: TEdit;
  19.     Label1: TLabel;
  20.     Label2: TLabel;
  21.     Label3: TLabel;
  22.     Memo1: TMemo;
  23.     procedure Button1Click(Sender: TObject);
  24.     procedure Edit3KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  25.     procedure FormCreate(Sender: TObject);
  26.   private
  27.     { private declarations }
  28.   public
  29.     { public declarations }
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.   sock: TTCPBlockSocket;
  35.   buffer: string;
  36.  
  37. implementation
  38.  
  39. {$R *.lfm}
  40.  
  41. { TForm1 }
  42.  
  43. procedure TForm1.FormCreate(Sender: TObject);
  44. begin
  45.   edit1.clear;
  46.   edit2.clear;
  47.   edit3.clear;
  48.   memo1.lines.clear;
  49. end;
  50.  
  51. procedure TForm1.Button1Click(Sender: TObject);
  52. begin
  53.   sock := TTCPBlockSocket.Create;
  54.   sock.Connect(edit1.text, edit2.text);
  55.  
  56.   if sock.LastError <> 0 then
  57.   begin
  58.     memo1.lines.add('Could not connect to server.');
  59.     //halt(1);
  60.   end;
  61.  
  62.    if sock.LastError = 0 then
  63.   begin
  64.     memo1.lines.add('Connected');
  65.     //halt(1);
  66.   end;
  67.  
  68.  
  69. end;
  70.  
  71. procedure TForm1.Edit3KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  72. begin
  73.   if Key = 13 { or VK_RETURN  and add lcltype in your uses }  then
  74.   begin
  75.     buffer := edit3.text;//'hello, this is a client';
  76.     sock.SendString(buffer + CRLF);
  77.     memo1.lines.add('Me : '+buffer);
  78.   end;
  79. end;
  80.  
  81. end.                                                    

server
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, blcksock;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Edit1: TEdit;
  17.     Edit2: TEdit;
  18.     Label1: TLabel;
  19.     Label2: TLabel;
  20.     Memo1: TMemo;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure FormCreate(Sender: TObject);
  23.     procedure HandleConnection(ASocket: TTCPBlockSocket);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.   ListenerSocket: TTCPBlockSocket;
  33.   ConnectionSocket: TTCPBlockSocket;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. procedure TForm1.FormCreate(Sender: TObject);
  42. begin
  43.   edit1.clear;
  44.   edit2.clear;
  45.   memo1.lines.Clear;
  46. end;
  47.  
  48. procedure TForm1.Button1Click(Sender: TObject);
  49. begin
  50.   ListenerSocket := TTCPBlockSocket.Create;
  51.   ConnectionSocket := TTCPBlockSocket.Create;
  52.   ListenerSocket.CreateSocket;
  53.   ListenerSocket.setLinger(true,10);
  54.   ListenerSocket.bind(edit1.text ,edit2.text);
  55.   ListenerSocket.listen;
  56.   memo1.lines.add('waiting');
  57.   repeat
  58.     Application.ProcessMessages;
  59.     if ListenerSocket.canread(1000) then // <--- use 100 here instead of 1000
  60.     begin
  61.       memo1.lines.add('Incoming connection');
  62.       ConnectionSocket := TTCPBlockSocket.Create; // multiple new ones
  63.       ConnectionSocket.Socket := ListenerSocket.accept;
  64.       //memo1.lines.add('Client connected on local port: ', ConnectionSocket.LocalSin.sin_port, ' ', ConnectionSocket.RemoteSin.sin_port);
  65.       HandleConnection(ConnectionSocket);
  66.       ConnectionSocket.CloseSocket;
  67.       ConnectionSocket.Free;
  68.       memo1.lines.add('Connection closed');
  69.       memo1.lines.add('Waiting for new connection');
  70.     end;
  71.   until false;
  72.  
  73.   ListenerSocket.Free;
  74.   memo1.lines.add('done');
  75.  
  76. end;
  77.  
  78. procedure TForm1.HandleConnection(ASocket: TTCPBlockSocket);
  79. var
  80.   S: String;
  81. begin
  82.    memo1.lines.add('Received client text:');
  83.   repeat
  84.     S := ASocket.RecvString(120000);
  85.     memo1.lines.add('Client : '+S);
  86.     ASocket.SendString('Ok' + CRLF);
  87.   until ASocket.lasterror <> 0;
  88. end;
  89.  
  90. end.
  91.                          

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #64 on: October 21, 2017, 11:01:26 am »
Ok, the sluggishness is to be expected. But you stated it happens after  like 5 times. It should already happen after the initial connection (thus the first time the client sends data).

Let look at the following:
Code: Pascal  [Select][+][-]
  1.   repeat
  2.     S := ASocket.RecvString(120000);
  3.     memo1.lines.add('Client : '+S);
  4.     ASocket.SendString('Ok' + CRLF);
  5.   until ASocket.lasterror <> 0;

You need to know that any loop that you create has the potential to block the entire program. And sockets in Synapse are "blocking". (Why do you think they are called TBlockSockets)

Let me explain this another way. If you have a procedure and you do a loop in it, the program has no change to do anything else. You program is running in one "thread". So all commands you see are executed after each other. So a loop in your procedure will block the program from responding to anything else.

Try to but a button on your form with the following:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   I: Integer;
  4. begin
  5.   Button1.Caption := 'Busy';
  6.   for I := 1 to 10 do
  7.   begin
  8.     sleep(1000); // 1000ms is 1 second
  9.   end;
  10.   Button1.Caption := 'Ok';
  11. end;
Now run the program and press that button. What do you see? Does the program still respons? Wait a bit... until the button-text goes "Ok". Does your program respond now? Yes.

So while running a loop in your program, your program stops all other execution (like responding to your mouse).

We can fix that with a Application.Processmessages;
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   I: Integer;
  4. begin
  5.   Button1.Caption := 'Busy';
  6.   for I := 1 to 10 do
  7.   begin
  8.     Application.Processmessages; // <-- add this
  9.     sleep(1000); // 1000ms is 1 second
  10.   end;
  11.   Button1.Caption := 'Ok';
  12. end;
Now you can see that you can move the form while the loop is running... but the program is still a bit sluggish. That's because the sleep(1000) still holds the program captive for that one second each time. And we can't insert an processmessages in that sleep().

Now loop again at your procedure/loop:
Code: Pascal  [Select][+][-]
  1.   repeat
  2.     S := ASocket.RecvString(120000); // this acts just like a sleep
  3.     memo1.lines.add('Client : '+S);
  4.     ASocket.SendString('Ok' + CRLF);
  5.   until ASocket.lasterror <> 0;

Because RecvString tries to receive a string it waits until that string arrives or times out after 120000ms (=120 seconds).
So essentially you get this (IF the client doesn't send a string):
Code: Pascal  [Select][+][-]
  1.   repeat
  2.     sleep(120000); // 120 seconds
  3.     memo1.lines.add('Client : '+S);
  4.     ASocket.SendString('Ok' + CRLF);
  5.   until ASocket.lasterror <> 0;
You see, that's almost the same code as the blocking loop.

If the client does send a string your program responds for a moment but very shortly it freezes up again.

There are two possible solutions (actually three).
One, Reduce the timeout on RecvString() to something very short.
Like RecvString(10)
In the beginning that will work but if the string is large, it will timeout before the string is received completely.

Two (better solution), check with CanRead(10) if there is data on the line. Here you can use a small timeout because it's just a check if there is data. The actual reading of the string can have a bigger timeout. So:
Code: Pascal  [Select][+][-]
  1. repeat
  2.   if ASocket.CanRead(10) then
  3.   begin
  4.     S := ASocket.RecvString(120000);
  5.     memo1.lines.add('Client : '+S);
  6.     ASocket.SendString('Ok' + CRLF);
  7.   end;
  8.   Apllication.Processmessages; // let's not forget this one, otherwise it still hangs
  9. until ASocket.lasterror <> 0;
That would already be a major improvement. But it still wait a very small amount.

Three (best solutions), use threads. But first see if you understand all the above (so first try solution two). Then you can think about threads

Do you already know what threads are???

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #65 on: October 21, 2017, 11:39:04 am »
wow thank you for clear explanation, i understood all the explanations
and nope never heard of thread i will try to google it right now

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #66 on: October 21, 2017, 11:50:24 am »
i have a question
this is the code from server
Code: Pascal  [Select][+][-]
  1. if ListenerSocket.canread(100) then // <--- use 100 here instead of 1000
  2.     begin
  3.       memo1.lines.add('Incoming connection');
  4.       ConnectionSocket := TTCPBlockSocket.Create; // multiple new ones
  5.       ConnectionSocket.Socket := ListenerSocket.accept;
  6.       //memo1.lines.add('Client connected on local port: ',  
  7.       ConnectionSocket.LocalSin.sin_port, ' ',
  8.       ConnectionSocket.RemoteSin.HandleConnection(ConnectionSocket);
  9.       ConnectionSocket.CloseSocket;
  10.       ConnectionSocket.Free;
  11.       memo1.lines.add('Connection closed');
  12.       memo1.lines.add('Waiting for new connection');
  13.     end;  
so it says "if ListenerSocket can read begin
and then there is this code inside that if statement
Code: Pascal  [Select][+][-]
  1. HandleConnection(ConnectionSocket);
which is
Code: Pascal  [Select][+][-]
  1. procedure TForm1.HandleConnection(ASocket: TTCPBlockSocket);
  2. var
  3.   S: String;
  4. begin
  5.   memo1.lines.add('Received client text:');
  6.   repeat
  7.   if ASocket.CanRead(10) then
  8.   begin
  9.     S := ASocket.RecvString(120000);
  10.     memo1.lines.add('Client : '+S);
  11.     ASocket.SendString('Ok' + CRLF);
  12.   end;
  13.   Application.Processmessages; // let's not forget this one, otherwise it still hangs
  14. until ASocket.lasterror <> 0;  
and it says if ASocket.CanRead(10) then begin where asocket = connectionsocket

and my question is why do you check twice if the socket can read?

also

Code: Pascal  [Select][+][-]
  1. ConnectionSocket.Socket := ListenerSocket.accept;
what does that do?


rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #67 on: October 21, 2017, 12:23:34 pm »
and my question is why do you check twice if the socket can read?
The function of canread() is twofold. If is true when there is an incoming connection but it's also true when there is data on the line. It's false if there is no data on the line.

So after we establish a connection, the client may not send a string. But if we have RecvString() we wait in the main thread until that string is received. If we don't want to wait we can check with CanRead(10) if there is data. It has a very short waittime (10ms) and if there is no data it will return in 10ms.

That's why there are two CanReads. One is for detecting the connection and the other is for detecting if there is really data on the line.

Quote
Code: Pascal  [Select][+][-]
  1. ConnectionSocket.Socket := ListenerSocket.accept;
what does that do?
I thought we already discussed this.
The ListenerSocket is used to wait and listen on a certain port for incoming connections. If we would accept the connection with that same socket, we can't listen anymore. The socket would be used for the connection and in that case you can only have one connection. With the ListenerSocket.accept there is a new second socket created with the incoming connection. That connection is put in ConnectionSocket. The ListenerSocket can go on and keep on listening for other incoming connections. (You said you have multiple clients so the server needs to keep on listening)

Accept function waits for an incoming connection (if it's not already waiting) and passes it on to the caller:
So the following code would be valid to receive multiple connection.
Only we don't do anything with ConnectionSocket1, ConnectionSocket2 etc. but the connections are established.
Code: Pascal  [Select][+][-]
  1. ConnectionSocket1.Socket := ListenerSocket.accept;
  2. ConnectionSocket2.Socket := ListenerSocket.accept;
  3. ConnectionSocket3.Socket := ListenerSocket.accept;
  4. ConnectionSocket4.Socket := ListenerSocket.accept;
  5. ConnectionSocket5.Socket := ListenerSocket.accept;
This code will connect to 5 clients. But it will also wait until those 5 clients are really connected. It does not handle the connections yet. So this code is really silly but it shows that you can pass the incoming connection to a different socket each time and in the end you have 5 working connections.

Now what we want to do is handle the connection in between those connect-actions:
Code: Pascal  [Select][+][-]
  1. ConnectionSocket1.Socket := ListenerSocket.accept;
  2. HandleConnection(ConnectionSocket1);
  3. ConnectionSocket2.Socket := ListenerSocket.accept;
  4. HandleConnection(ConnectionSocket2);
  5. ConnectionSocket3.Socket := ListenerSocket.accept;
  6. HandleConnection(ConnectionSocket3);
  7. ConnectionSocket4.Socket := ListenerSocket.accept;
  8. HandleConnection(ConnectionSocket4);
  9. ConnectionSocket5.Socket := ListenerSocket.accept;
  10. HandleConnection(ConnectionSocket5);
Now the problem is that HandleConnection() is a procedure with a loop. And as we already said before, a loop is blocking. So if you look at the code above you'll see that with HandleConnection for ConnectionSocket1 you are handling ConnectionSocket1 but the code for connecting to the second client doesn't run. And that's a problem if you want to connect to multiple clients at the same time.

Now if we do the following:
Code: Pascal  [Select][+][-]
  1. ConnectionSocket1.Socket := ListenerSocket.accept;
  2. // put in a thread and HandleConnection(ConnectionSocket1);
  3. ConnectionSocket2.Socket := ListenerSocket.accept;
  4. // put in a thread and HandleConnection(ConnectionSocket2);
  5. ConnectionSocket3.Socket := ListenerSocket.accept;
  6. // put in a thread and HandleConnection(ConnectionSocket3);
  7. ConnectionSocket4.Socket := ListenerSocket.accept;
  8. // put in a thread and HandleConnection(ConnectionSocket4);
  9. ConnectionSocket5.Socket := ListenerSocket.accept;
  10. // put in a thread and HandleConnection(ConnectionSocket5);
With // put in a thread  I mean that the ConnectionSocketX is passed on to a thread and handled there. In the meantime the main thread can continue and listen for more incoming connections.

O, we haven't learned about threads yet... that's next. I was already typing an example but first..
Do you understand the first half of my explanation here ?

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #68 on: October 21, 2017, 12:58:52 pm »
yes i understood first half of your explanation

i now made server to send string to client and i think it works okay but can you check if i need to fix something?

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #69 on: October 21, 2017, 01:26:34 pm »
i now made server to send string to client and i think it works okay but can you check if i need to fix something?
No, as fas as I can see it all works.

But with one comment...
Your Button1Click establishes the connection and as you can see you keep that procedure/event running with the loop to receive data. And letting an event-procedure run that long is really bad programming. An event-procedure should end as quickly as possible.

In this simple example with one server and one client you could move the repeat-loop for the server and client in both Button1Click to a TTimer with a interval of say 100ms. That way the time will check if there is something to receive. At the end of Button1Click, you enable the TTimer.

But first try to change your code with the timer.

Regarding multiple connections... you could even add an array of sockets and listen for all those sockets in the timer. But that's for later. Eventually all the Application.ProcessMessages should be eliminated and replaced by either TTimer of TThreads.

(First we could try to use a socket array with a TTimer and after that you might be ready for threads, which are "cleaner")
« Last Edit: October 21, 2017, 01:29:44 pm by rvk »

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #70 on: October 21, 2017, 01:50:25 pm »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   s:string;
  4. begin
  5.    if Sock.canread(100) then
  6.       begin
  7.         S := sock.RecvString(120000);
  8.         memo1.lines.add('server : '+S);
  9.       end;
  10. end;        

like this?

btw
Code: Pascal  [Select][+][-]
  1. if sock.LastError = 0 then
that means it is not connected right? i'm kinda confused

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #71 on: October 21, 2017, 02:28:55 pm »
btw
Code: Pascal  [Select][+][-]
  1. if sock.LastError = 0 then
that means it is not connected right? i'm kinda confused
LastError = 0 means there is NO error so that means the connection is still there.

You shown Timer1Timer should work for the client but you only need to enable the timer AFTER the connection is made. So set the Enabled in the object inspector to false and set it to true in code after you made the connection.

shs

  • Sr. Member
  • ****
  • Posts: 310
Re: can i make server?
« Reply #72 on: October 21, 2017, 02:31:53 pm »
Quote
LastError = 0 means there is NO error so that means the connection is still there.
that's what i thought but when i compiled it, it was opposite so i changed the code

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3. s : string;
  4. begin
  5.   sock := TTCPBlockSocket.Create;
  6.   sock.Connect(edit1.text, edit2.text);
  7.   sock.listen;
  8.  
  9.    if sock.LastError = 0 then
  10.   begin
  11.     memo1.lines.add('Could not connect to server.');
  12.     //halt(1);
  13.   end;
  14.  
  15.    if sock.LastError <> 0 then
  16.   begin
  17.     memo1.lines.add('Connected to the server');
  18.     memo1.lines.add('...');
  19.     memo1.lines.add('Say Something');
  20.     edit3.enabled:=true;
  21.     timer1.enabled:=true;
  22.     //halt(1);
  23.   end;
  24. end;                

mai

  • Full Member
  • ***
  • Posts: 133
  • truther
Re: can i make server?
« Reply #73 on: October 21, 2017, 02:32:11 pm »
can I haz cheezburger ?

rvk

  • Hero Member
  • *****
  • Posts: 6169
Re: can i make server?
« Reply #74 on: October 21, 2017, 02:42:56 pm »
Quote
LastError = 0 means there is NO error so that means the connection is still there.
that's what i thought but when i compiled it, it was opposite so i changed the code
No, that's wrong.
Sock.LastError is really an error.
If you print out that error in the memo you can see what error it is.
Code: Pascal  [Select][+][-]
  1.   if sock.LastError <> 0 then
  2.   begin
  3.     memo1.lines.add('Could not connect to server.' + sock.LastError.ToString);
  4.     //halt(1);
  5.   end;

My guess is that you get a 10056 error.
And guess what... that means Socket already connected.
So your socket is already connected after the last socket command you give.

Let's look at it:
Code: Pascal  [Select][+][-]
  1.   sock := TTCPBlockSocket.Create;
  2.   sock.Connect(edit1.text, edit2.text);
  3.   sock.listen;
What are you doing in that last line. The sock.listen is only for the server.
See http://synapse.ararat.cz/doc/help/blcksock.TTCPBlockSocket.html#Listen

It puts the socket in listening mode FOR NEW INCOMING CONNECTIONS.
But for the client you already issued a Connect to the server on line #2. So the socket is already connected.

The sock.listen is only needed for the ListeningSocket on the server side because that's the only one that's listening for new incoming connections. The following ConnectionSocket on the server and the sock on the client don't need to listen for new connections because they already have a connections.

 

TinyPortal © 2005-2018