Freepascal socket unit use lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Jack.R · 2019-11-01 15:39 Dear All, Many years ago, I was programming with Delphi. I restart programming now with Lazarus and need to play with sockets. The expected application will have a GUI which will display some data got through socket, can be a server or a client and must accept many client at the same time when acting as a server When you search what to use, you found sockets unit, synapse or indy. I qucikly try those 3. I remove indy as it seems API change between versions and I do not want to rewrite the application at each new indy version. I try to decide if I go to sockets unit or synapse. The example I have found in the sockets unit documentation are relative to older version and deprecated functions. I try to build below working example. Currently the server is single thread, so accept a single client but the goal is to write also a multithread one to accept many clients. My current issue is to detect on the server side that the client has close the connection in order to close the socket and came back to listening for another client connection. Could someone point me on what to check to detect client disconnection ? Server 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 program server; {  Program to test sockets and get working example of Sockets unit.  This is the single thread server part.  Use the client part to send data to that server.  Inspired from fpConnect / fpAccept examples} uses  Sockets,  SysUtils; var  // Sockets descriptors  SockDesc: longint;  clientId: longint;  // Sockets addresses  SockAddr: TInetSockAddr;  ClientAddr: TInetSockAddr;  // Message  Buffer: string [255];   AddrSize: longint;  RecvSize: longint;  StartTimer: longint;   procedure perror(const S: string);  var    ErrorMsg: string;  begin    case socketerror of      EsockADDRINUSE: ErrorMsg := 'Error number when socket address is already in use';    EsockEACCESS: ErrorMsg := 'Access forbidden error';      EsockEBADF: ErrorMsg := 'Alias: bad file descriptor';      EsockEFAULT: ErrorMsg := 'Alias: an error occurred';      EsockEINTR: ErrorMsg := 'Alias : operation interrupted';      EsockEINVAL: ErrorMsg := 'Alias: Invalid value specified';      EsockEMFILE: ErrorMsg := 'Error code ?';      EsockEMSGSIZE: ErrorMsg := 'Wrong message size error';      EsockENOBUFS: ErrorMsg := 'No buffer space available error';      EsockENOTCONN: ErrorMsg := 'Not connected error';      EsockENOTSOCK: ErrorMsg := 'File descriptor is not a socket error';      EsockEPROTONOSUPPORT: ErrorMsg := 'Protocol not supported error';    EsockEWOULDBLOCK: ErrorMsg := 'Operation would block error';      else        ErrorMsg := 'Undescribed error : ' + IntToStr(socketerror);    end;    writeln(S, ErrorMsg);    halt(100);  end; begin  SockDesc := fpSocket(AF_INET, SOCK_STREAM, 0);  if SockDesc = -1 then    Perror('[Server] Socket : ');  SockAddr.sin_family := AF_INET;  // Port 8008  SockAddr.sin_port := htons(8008);  // Look on all interfaces so address 0.0.0.0  SockAddr.sin_addr := StrToNetAddr('0.0.0.0');  // Bind socket  if fpBind(SockDesc, @SockAddr, sizeof(SockAddr)) = -1 then    PError('[Server] Bind : ');  // Turn socket into listening state  if fpListen(SockDesc, 1) = -1 then    PError('[Server] Listen : ');  // Waiting client connection  Writeln('Waiting for client connection, run now client in an other tty');  // Accept client connection  AddrSize := sizeof(ClientAddr);  clientId := fpAccept(SockDesc, @ClientAddr, @AddrSize);  if clientId = -1 then    PError('[Server] Accept : ' + NetAddrToStr(ClientAddr.sin_addr));  // Read data  RecvSize := fprecv(clientId, @Buffer, 255, 0);  while RecvSize <> -1 do  begin    if RecvSize > 0 then      writeln('[Server] Receive : ' + buffer + ' [' + IntToStr(RecvSize) + ']');    RecvSize := fprecv(clientId, @Buffer, 255, 0);  end;  if RecvSize = -1 then    PError('[Server] Read failed : ');end.   Client 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 program client; {  Program to test sockets and get working example of Sockets unit.  This is the client part. The server part must be start before using this one.  Inspired from fpConnect / fpAccept examples} uses  Sockets,  SysUtils;   procedure PError(const S: string);  var    ErrorMsg: string;  begin    case socketerror of      EsockADDRINUSE: ErrorMsg := 'Error number when socket address is already in use';    EsockEACCESS: ErrorMsg := 'Access forbidden error';      EsockEBADF: ErrorMsg := 'Alias: bad file descriptor';      EsockEFAULT: ErrorMsg := 'Alias: an error occurred';      EsockEINTR: ErrorMsg := 'Alias : operation interrupted';      EsockEINVAL: ErrorMsg := 'Alias: Invalid value specified';      EsockEMFILE: ErrorMsg := 'Error code ?';      EsockEMSGSIZE: ErrorMsg := 'Wrong message size error';      EsockENOBUFS: ErrorMsg := 'No buffer space available error';      EsockENOTCONN: ErrorMsg := 'Not connected error';      EsockENOTSOCK: ErrorMsg := 'File descriptor is not a socket error';      EsockEPROTONOSUPPORT: ErrorMsg := 'Protocol not supported error';    EsockEWOULDBLOCK: ErrorMsg := 'Operation would block error';      else        ErrorMsg := 'Undescribed error : ' + IntToStr(socketerror);    end;    writeln(S, ErrorMsg);  end; var  // Socket descriptor  SockDesc: longint;  // Socket address  SockAddr: TInetSockAddr;  // Message  Buffer: array [0..255] of char;  //string[255];  i: longint;  Result: longint; begin  SockDesc := fpSocket(AF_INET, SOCK_STREAM, 0);  if SockDesc = -1 then    Perror('[Client] Socket : ');  SockAddr.sin_family := AF_INET;  // Port 8008  SockAddr.sin_port := htons(8008);  // Address 127.0.0.1  SockAddr.sin_addr := StrToNetAddr('127.0.0.1');  // Connection  if fpconnect(SockDesc, @SockAddr, SizeOf(SockAddr)) = -1 then    PError('[Client] Connect : ');  // Send data  for i := 1 to 100 do  begin    Buffer := 'This is a textstring sent by the client. (' + IntToStr(i) + ')';    Result := fpsend(SockDesc, @Buffer, SizeOf(Buffer), 1);    writeln(Buffer + ' [' + IntToStr(Result) + ']');    if Result <> SizeOf(Buffer) then      PError('[Client] Send : ');    sleep(5);  end;  // Shutdown communication  if fpshutdown(SockDesc, 2) = -1 then    PError('[Client] Shutdown : ');  // Close socket  if CloseSocket(SockDesc) = -1 then    PError('[Client] Close : ');end. ---------------------------------------------------------------------- Thaddy · 2019-11-01 15:51 the standard fcl-net package comes with many examples. ---------------------------------------------------------------------- Jack.R · 2019-11-01 16:56 Where did you find them ? In the examples folder of fcl-net on my installation I have : cnslookup.pp, ip6test.pp, rpccli.pp, rpcserv.pp, svrclass.pp, svrclass_xmlrpc.pp,testdns.pp, testhosts.pp, testhst.pp, testnet.pp, testproto.pp, testsvc.pp, testuri.pp I have had a look but they did not seems to suit my needs. fpSock.pp seems to be interresting but the only example of use I found was "fcl-net, the undocumented treasure" on "Pascal Warrior's Journey ". That is why I get back to the socket unit. Did I make a mistake and should invertigate more deeper to use fpSock ? ---------------------------------------------------------------------- Jack.R · 2019-11-03 13:45 fpSock from fcl-net is unix only as it has a use clause with baseunix,Unix; Regarding my original issue,'Detecting on the server side that the client has disconnect', we are using blocking sockets, this means that if fpReceive return a 0 size, the client has disconnect gracefully. The code thus became: 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 program server; {  Program to test sockets and get working example of Sockets unit.  This is the single thread server part.  Use the client part to send data to that server.  Inspired from fpConnect / fpAccept examples} uses  Sockets,  SysUtils,  baseunix; var  // Sockets descriptors  SockDesc: longint;  clientId: longint;  // Sockets addresses  SockAddr: TInetSockAddr;  ClientAddr: TInetSockAddr;  // Message  Buffer: string [255];   AddrSize: longint;  RecvSize: longint;  StartTimer: longint;   procedure perror(const S: string);  var    ErrorMsg: string;  begin    case socketerror of      EsockADDRINUSE: ErrorMsg := 'Error number when socket address is already in use';      EsockEACCESS: ErrorMsg := 'Access forbidden error';      EsockEBADF: ErrorMsg := 'Alias: bad file descriptor';    EsockEFAULT: ErrorMsg := 'Alias: an error occurred';      EsockEINTR: ErrorMsg := 'Alias : operation interrupted';      EsockEINVAL: ErrorMsg := 'Alias: Invalid value specified';      EsockEMFILE: ErrorMsg := 'Error code ?';      EsockEMSGSIZE: ErrorMsg := 'Wrong message size error';      EsockENOBUFS: ErrorMsg := 'No buffer space available error';      EsockENOTCONN: ErrorMsg := 'Not connected error';      EsockENOTSOCK: ErrorMsg := 'File descriptor is not a socket error';      EsockEPROTONOSUPPORT: ErrorMsg := 'Protocol not supported error';      EsockEWOULDBLOCK: ErrorMsg := 'Operation would block error';      else        ErrorMsg := 'Undescribed error : ' + IntToStr(socketerror);    end;    writeln(S, ErrorMsg);    halt(100);  end; begin  SockDesc := fpSocket(AF_INET, SOCK_STREAM, 0);  if SockDesc = -1 then    Perror('[Server] Socket : ');  SockAddr.sin_family := AF_INET;  // Port 8008  SockAddr.sin_port := htons(8008);  // Look on all interfaces so address 0.0.0.0  SockAddr.sin_addr := StrToNetAddr('0.0.0.0');  // Bind socket  if fpBind(SockDesc, @SockAddr, sizeof(SockAddr)) = -1 then    PError('[Server] Bind : ');  // Turn socket into listening state  if fpListen(SockDesc, 1) = -1 then    PError('[Server] Listen : ');  // Waiting client connection  Writeln('Waiting for client connection, run now client in an other tty');  // Accept client connection  AddrSize := sizeof(ClientAddr);   while True do  begin    clientId := fpAccept(SockDesc, @ClientAddr, @AddrSize);    if clientId = -1 then    PError('[Server] Accept : ' + NetAddrToStr(ClientAddr.sin_addr))  else      writeln('[Server] New client connected.');    // Read data  RecvSize := fprecv(clientId, @Buffer, 255, 0);    while RecvSize > 0 do    begin      if RecvSize > 0 then        writeln('[Server] Receive : ' + buffer + ' [' + IntToStr(RecvSize) + ']');      RecvSize := fprecv(clientId, @Buffer, 255, 0);    end;    if (RecvSize = 0) and (SocketError = 0) then    begin      writeln('[Server] Client disconnect.');      writeln('');    end;    if RecvSize = -1 then    PError('[Server] Read failed : ');  end;end.