(SOLVED - THANKS FOR HELP)connecting a server socket with several client sockets lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Nukualofa · 2015-11-24 07:59 Hey, I want to write a simple Chat using the Sockets unit. I have four files: Server, ServerThread, Client & ClientThread. The server creates a socket and waits for clients to connect to it. As soon as a client connects, a new serverThread is started that manages the communication with the client. The client also creates a socket, connects to the server and starts a clientThread that waits for incoming messages from the corresponding serverThread. Everything works fine if I use a different port for each client. But Google says, the normal way is to have only one port (on the server side) which can handle several clients. If a second client tries to connect to the port, there is no SocketError, but it doesn't work anyway. Apparently the fpaccept-function that waits for a client to connect isn't reacting to the client's fpconnect-function. I spent a few days reading everything I found about the topic and tried almost everything but failed miserably. I would be really glad if someone could tell me what's wrong with my code. Here it is (sorry, the "insert code" button seems not to work): unit Server; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Sockets, ServerThread; type   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   public     procedure handle(ID, message: String);   end; var   Form1: TForm1;   threads: Array[1..4] of TServerThread;   serverSocket: Longint;   clientSocket: Longint;   serverAddr: TInetSockAddr;   opt: Integer = 1;   addrSize: Longint;   clientCount: Integer = 0; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); begin   repeat     serverSocket:= fpSocket(AF_INET, SOCK_STREAM, 0);     if fpSetSockOpt(serverSocket, SOL_SOCKET, SO_REUSEADDR, @opt, sizeOf(opt)) = SOCKET_ERROR then showMessage('Server : Multi : ' + intToStr(socketError));     if serverSocket = SOCKET_ERROR then showMessage('Server : Socket : ' + intToStr(socketError));     serverAddr.sin_family:= AF_INET;     serverAddr.sin_port:= htons(50000);     serverAddr.sin_addr.s_addr:= htonl($7F000001);     if fpBind(serverSocket, @serverAddr, sizeOf(serverAddr)) = SOCKET_ERROR then showMessage('Server : Bind : ' + intToStr(socketError));     if fpListen(serverSocket, 4) = SOCKET_ERROR then showMessage('Server : Listen : ' + intToStr(socketError));     showMessage('Waiting for connect from Client...');     addrSize:= sizeOf(serverAddr);     clientSocket:= fpaccept(serverSocket, @serverAddr, @addrSize);     if clientSocket = SOCKET_ERROR then showMessage('Server : Accept : ' + intToStr(socketError)) else clientCount:= clientCount + 1;     threads[clientCount]:= TServerThread.create(true, clientSocket);     threads[clientCount].start;   until clientCount = 4; end; procedure TForm1.handle(ID, message: String); var   i, toTerminate: Integer;   MyCriticalSection: TRTLCriticalSection; begin   InitCriticalSection(MyCriticalSection);   EnterCriticalSection(MyCriticalSection);   try     for i:= 1 to clientCount do     begin       threads.send(ID + ': ' + message);       if threads.getID = ID then toTerminate:= i;     end;     if message = 'ciao' then     begin       threads[toTerminate].send('ciao');       threads[toTerminate].close;       clientCount:= clientCount - 1;       for i:= toTerminate to clientCount do threads:= threads[i + 1];     end;   finally     LeaveCriticalSection(MyCriticalSection);   end; end; end. unit ServerThread; {$mode objfpc}{$H+} interface uses Classes, Dialogs, Sockets, SysUtils; type   TServerThread = class(TThread)   private     ID: String;     clientSocket: Longint;   protected     procedure execute; override;   public     constructor create(createSuspended: Boolean; client: Longint);     procedure send(msg: String);     function getID: String;     procedure close;   end; var   buffer: String[255];   count: Longint; implementation uses Server; constructor TServerThread.create(createSuspended: Boolean; client: Longint); begin   freeOnTerminate:= true;   inherited create(createSuspended);   clientSocket:= client; end; procedure TServerThread.execute; begin   count:= fprecv(clientSocket, @buffer[1], 255, 0);   if (count <> SOCKET_ERROR) and (count > 0) then   begin     setLength(buffer, count);     ID:= buffer;   end;   buffer:= 'Herzlich willkommen im Chat, ' + ID;   count:= length(buffer);   if fpsend(clientSocket, @buffer[1], count, 0) = count then   begin     repeat       count:= fprecv(clientSocket, @buffer[1], 255, 0);       if (count <> SOCKET_ERROR) and (count > 0) then       begin         setLength(buffer, count);         Form1.handle(ID, buffer);       end;     until (count = SOCKET_ERROR) or (count = 0);   end; end; procedure TServerThread.send(msg: String); begin   fpsend(clientSocket, @msg[1], length(msg), 0); end; function TServerThread.getID: String; begin   result:= ID; end; procedure TServerThread.close; begin   closeSocket(clientSocket); end; end. unit Client; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Sockets, ClientThread; type   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     Button2: TButton;     Edit1: TEdit;     Edit2: TEdit;     Memo1: TMemo;     procedure Button1Click(Sender: TObject);     procedure Button2Click(Sender: TObject);     procedure Edit1Change(Sender: TObject);     procedure handle(msg: String);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1;   thread: TClientThread;   serverAddr: TInetSockAddr;   serverSocket: Longint; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); begin   serverSocket:= fpSocket(AF_INET, SOCK_STREAM, 0);   if serverSocket = SOCKET_ERROR then showMessage('Client : Socket : ' + intToStr(socketError));   serverAddr.sin_family:= AF_INET;   serverAddr.sin_port:= htons(50000);   serverAddr.sin_addr.s_addr:= htonl($7F000001);   //funktioniert beim zweiten Client nicht, da kein Error, obwohl die Verbindung nicht zustande kommt (fpaccept reagiert nicht)   if fpconnect(serverSocket, @serverAddr, sizeOf(serverAddr)) = SOCKET_ERROR then showMessage('Client : Connect : ' + intToStr(socketError));   thread:= TClientThread.create(true, serverSocket);   thread.start;   buffer:= Edit1.Text;   fpsend(serverSocket, @buffer[1], length(buffer), 0);   Button2.Enabled:= true; end; procedure TForm1.Button2Click(Sender: TObject); var   buffer: String; begin   buffer:= Edit2.Text;   fpsend(serverSocket, @buffer[1], length(buffer), 0); end; procedure TForm1.Edit1Change(Sender: TObject); begin   if length(Edit1.Text) > 0 then Button1.Enabled:= true else Button1.Enabled:= false; end; procedure TForm1.handle(msg: String); begin   if msg = 'ciao' then closeSocket(serverSocket)   else Memo1.Lines.Add(msg); end; end. unit ClientThread; {$mode objfpc}{$H+} interface uses Classes, Dialogs, Sockets, SysUtils; type   TClientThread = class(TThread)   private     serverSocket: Longint;   protected     procedure execute; override;   public     constructor create(createSuspended: Boolean; server: Longint);   end; var   buffer: String[255];   count, i: Longint; implementation uses Client; constructor TClientThread.create(createSuspended: Boolean; server: Longint); begin   freeOnTerminate:= true;   inherited create(createSuspended);   serverSocket:= server; end; procedure TClientThread.execute; begin   repeat     count:= fprecv(serverSocket, @buffer[1], 255, 0);     if count <> SOCKET_ERROR then     begin       setLength(buffer, count);       Form1.handle(buffer);     end;   until buffer = 'ciao';   closeSocket(serverSocket); end; end. ---------------------------------------------------------------------- SymbolicFrank · 2015-11-24 08:09 If you want multiple connections, you need multiple ports. I think the issue is, that there has to be a single port on the server that accepts incoming requests. But the server will then spawn a new thread and assign it a new port, exclusively for that session. ---------------------------------------------------------------------- Nukualofa · 2015-11-24 09:32 Ok, but if Client A has "used" the single port on the server that accepts incoming requests, this won't work for Client B, except there is a way to "reset" the socket or the port after Client A has been assigned a new port. Is there a simpler solution than closing and reopening the socket? ---------------------------------------------------------------------- JD · 2015-11-24 10:01 Pardon me but why don't you use Indy or Synapse to do this? They already have this multithreaded functionality built-in. JD ---------------------------------------------------------------------- rvk · 2015-11-24 10:05 Quote from: Nukualofa on November 24, 2015, 10:32:42 amOk, but if Client A has "used" the single port on the server that accepts incoming requests, this won't work for Client B, except there is a way to "reset" the socket or the port after Client A has been assigned a new port. Is there a simpler solution than closing and reopening the socket? ?? A server can receive multiple connection to one port. It's not the receiving port that makes it unique but the incoming and outgoing port (and incoming and outgoing IP). So a connection is made up of 4 numbers. All these numbers make a unique connection. So you can have this (every line with 4 numbers must be unique): 192.168.1.77:80 -> 192.168.1.1:80 192.168.1.77:81 -> 192.168.1.1:80 192.168.1.66:80 -> 192.168.1.1:80 192.168.1.66:81 -> 192.168.1.1:80 So when I (192.168.1.55) try to make a connection to 192.168.1.1:80 I give a local unique port at my end (for instance 1234) and connect to port 80 of the server, you get 192.168.1.55:1234 -> 192.168.1.1:80 See the question and answers here: http://stackoverflow.com/questions/3329641/how-do-multiple-clients- connect-simultaneously-to-one-port-say-80-on-a-server If this doesn't work for you there must be something else wrong in the code. (you can put [ code ] [/ code ] (without the spaces) around your code.) ---------------------------------------------------------------------- rvk · 2015-11-24 10:32 The problem you're having is that your trying to bind multiple listening-sockets to the same port in Button1Click. You should only create ONE listening-socket. When a client connects you get a unique Clientsocket from fpaccept and that one you put in your server thread. The listening server-socket keeps checking for new incoming connections. So you should put the initialization of the listening-socket outside the repeat loop. Something like this: (not tested) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin   serverSocket:= fpSocket(AF_INET, SOCK_STREAM, 0);  if fpSetSockOpt(serverSocket, SOL_SOCKET, SO_REUSEADDR, @opt, sizeOf(opt)) = SOCKET_ERROR then showMessage('Server : Multi : ' + intToStr(socketError));  if serverSocket = SOCKET_ERROR then showMessage('Server : Socket : ' + intToStr(socketError));  serverAddr.sin_family:= AF_INET;  serverAddr.sin_port:= htons(50000);  serverAddr.sin_addr.s_addr:= htonl($7F000001);  if fpBind(serverSocket, @serverAddr, sizeOf(serverAddr)) = SOCKET_ERROR then showMessage('Server : Bind : ' + intToStr(socketError));  if fpListen(serverSocket, 4) = SOCKET_ERROR then showMessage('Server : Listen : ' + intToStr(socketError));  showMessage('Waiting for connect from Client...');   repeat    addrSize:= sizeOf(serverAddr);    clientSocket:= fpaccept(serverSocket, @serverAddr, @addrSize);    if clientSocket = SOCKET_ERROR then showMessage('Server : Accept : ' + intToStr(socketError)) else clientCount:= clientCount + 1;    threads[clientCount]:= TServerThread.create(true, clientSocket);    threads[clientCount].start;  until clientCount = 4; end; (B.T.W. I would put a TMemo on the form and instead of showing ShowMessage put the messages in that TMemo.) ---------------------------------------------------------------------- Nukualofa · 2015-11-24 10:50 @rvk: that's it! I moved the "repeat" behind the "bind" and it works perfectly! Thank you so much! @everyone else: Thanks for your advice, too!