Trying, and failing, to create a TCP Client lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- geraldholdsworth · 2025-09-04 16:16 After pulling my hair out for a few hours now, I'm here to ask for help (which I'll probably solve myself now I've posted). I'm trying to use Synapse and write a simple application to connect to a device to control it...it is actually an Extron DMP 128 which one can conenct to over TCP. So, a simple TCP Client. Except, I keep getting "No route to host". And, I can't find any answers. Before now, I've written a TCP Server application, which was simple compared to this. I know it is not my firewall as the logs show the connection being allowed. Plus, doing the same thing with PacketSender works absolutely fine. Only difference is, with my application, the source IP is '0.0.0.0', but with PacketSender, it is my correct IP address (not sure if that is important or not). Entire unit is: 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 unit MainUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, ExtCtrls, StdCtrls, blcksock; type  { TMainForm }  TMainForm = class(TForm)   tbInputGain_ch1: TTrackBar;   Output: TMemo;   Button1: TButton;   procedure Button1Click(Sender: TObject);   procedure FormShow(Sender: TObject); private  DMPIPAddress: String;  DMPPort     : Integer; public  end; var MainForm: TMainForm; implementation {$R *.lfm} { TMainForm } procedure TMainForm.FormShow(Sender: TObject);begin DMPIPAddress:='192.168.0.210'; DMPPort     :=23;end; procedure TMainForm.Button1Click(Sender: TObject);var sock: TTCPBlockSocket; buffer: String = '';begin Output.Lines.Add('Connecting to '+DMPIPAddress+' on port '+IntToStr(DMPPort)); sock := TTCPBlockSocket.Create; sock.Connect(DMP IPAddress,IntToStr(DMPPort)); // Was there an error? if sock.LastError=0 then begin  repeat   buffer:=sock.RecvPacket(2000);   if not buffer.IsEmpty then Output.Lines.Add(buffer);  until buffer.IsEmpty;  Output.Lines.Add('Sending query');  sock.SendString('wG40001AU'#13#10);  repeat   buffer:=sock.RecvPacket(2000);   if not buffer.IsEmpty then Output.Lines.Add(buffer);  until buffer.IsEmpty; end else Output.Lines.Add('Could not connect : '+sock.LastErrorDesc); sock.Free;end; end.  This is on Lazarus 4 on macOS 15.6.1 ---------------------------------------------------------------------- 440bx · 2025-09-04 16:29 Quote from: geraldholdsworth on September 04, 2025, 06:16:33 pmAfter pulling my hair out for a few hours now, Lucky you... I couldn't pull my hair out for more than a few minutes now. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-04 17:20 Quote from: 440bx on September 04, 2025, 06:29:54 pmLucky you... I couldn't pull my hair out for more than a few minutes now. Trick is to have short hair so you can't grab a hold of it. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-05 11:34 I appear to have honed in on the problem...well, not the exact issue. Taking the same code and running it on a Windows PC and it runs fine and connects to the DSP without an issue. So, the problem is with macOS. However, PacketSender manages on a Mac, so there must be something I need to do, in the code, to persuade macOS to allow it to work. ---------------------------------------------------------------------- Zvoni · 2025-09-05 12:00 Did you "Bind" the socket to your local IP? After creating your socket call GetLocalSinIP/GetLocalSInPort It should show your local IP ---------------------------------------------------------------------- geraldholdsworth · 2025-09-05 14:40 Just tried. Same result.  :'( 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  DMPIPAddress:='192.168.0.210'; DMPPort     :=23; sock        :=TTCPBl ockSocket.Create; sock.Bind(sock.GetLocalSinIP,IntToStr(sock.GetLocalS inPort)); sock.Connect(DMPIPAddress,IntToStr(DMPPort)); ---------------------------------------------------------------------- Warfley · 2025-09-05 15:41 First, why do you use synapse if you only need a raw TCP Stream, FCL got you covered without any need for external/third party dependencies: 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 uses  ssockets;[...]var  sock: TInetSocket;begin  sock:=TInetSocket.Create('IP', 1337);  try    sock.Connect;    sock.Write('Hello World', Length('Hello World'));  finally    sock.Free;  end;end; Second, the error you are getting is directly coming from the underlying sockets API of the OS and it means the system cannot find a way to reach that IP address. There are a few options for that: 1. Your IP address is invalid 2. Your IP address may be read incorrectly (e.g. network and host byteorder mismatch) 3. Your device is not within a network that can reach that IP address. First verify that the IP address is reachable from your PC (e.g. ping it). If that is the case use the debugger to check how the IP is transformed internally and if the 32bit int representation is correct. If both is the case and it still doesn't work, maybe you need to sacrifice a goat or something to appease the demons living in the NIC ---------------------------------------------------------------------- geraldholdsworth · 2025-09-05 16:25 Hiya Warfley, Answer to your first question...well, there's two: I couldn't remember the native units, and I've always been told that Synapse is better. However, just tried what you've suggested and I get the same result: failure to connect (well, it fails when trying to create the socket, before it's even connected). I can easily connect, and talk to, the device in question from the Mac using PacketSender without an issue, using the same IP address and Port number. So, how does one use the debugger to check how the IP address is transformed? Finally, where can I get a goat? OK, I live in the Scottish Highlands...they wander wild up here! Do I need a special altar or something? ---------------------------------------------------------------------- Thaddy · 2025-09-05 16:39 Quote from: geraldholdsworth on September 05, 2025, 06:25:50 pm and I've always been told that Synapse is better. Although that has been the case for a very long time, it is also not true anymore for a very long time. About your issue: any security involved? Or is it just what you asked, a raw tcp connection? ---------------------------------------------------------------------- geraldholdsworth · 2025-09-05 17:23 I've removed the password from the device, so there will be no security involved. Didn't want to complicate things. ---------------------------------------------------------------------- Thaddy · 2025-09-05 17:38 In that case, is there a policy blocking a port? That can be a antivirus or a firewall. Usually these do not kick in on a local connection, though. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-05 17:54 I've looked at those. It's being allowed, according to the log. I've even turned off the Firewall (it was my first thought). I've largely discounted policies because PacketSender works fine to the same device. Code: [Select]Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 192.168.0.71:50282 192.168.0.210:23 TCP Out Allow outbound connections /Applications/PacketSender.app/Contents/MacOS/packetsender Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections /Applications/PacketSender.app/Contents/MacOS/packetsender Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 192.168.0.71:50270 192.168.0.210:23 TCP Out Allow outbound connections /Applications/PacketSender.app/Contents/MacOS/packetsender Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections /Applications/PacketSender.app/Contents/MacOS/packetsender Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections Communication allowed by rule Allowed 0.0.0.0 192.168.0.210:23 TCP Out Allow outbound connections This was the output from ESET at yesterday's attempts. ---------------------------------------------------------------------- marcov · 2025-09-05 19:53 Afaik policies on mac can be per app ? ---------------------------------------------------------------------- Warfley · 2025-09-05 20:44 Because I'm just at my phone for now and don't have access to any computer, so I can't give you the examples, but as you are on MacOS I'd do the following: 1. Check if netcat is working, its a command line utility shipped with MacOS that allows you to make easy network connections. Simply type "nc IP port" and it will open a TCP stream where you can send messages via the terminal. If netcat doesn't work there's a system issue. 2. MacOS is a POSIX system, meaning the system API for networking is the Berkeley Sockets API provided by the C ABI. So to remove as many variables as possible I'd try to create a simple C program that connects to the server. Don't even bother with IP strings and write the connection data right into the address struct. If that doesn't work it might be some app specific firewall 3. Now translate the C code 1-1 to Pascal. See the units sockets and baseunix which contain 1-1 equivalents of the C functions with the prefix fp (so instead of bind its fpbind) If this doesn't work it can indicate to be either an FPC issue or potentially a code signing issue (that apples official development toolchain for C avoids) ---------------------------------------------------------------------- geraldholdsworth · 2025-09-06 09:58 nc IP Port works.  I have no idea how to write in C. However, I found that this works: 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 Project1; uses Sockets, SysUtils; Var  SAddr    : TInetSockAddr;  Buffer   : string [255];  S        : Longint;  Sin,Sout : Text;  i      : integer; begin S:=fpSocket (AF_INET,SOCK_STREAM,0); if s<>-1 then begin  SAddr.sin_family:=AF_INET;  SAddr.sin_port:=htons(23);  SAddr.sin_addr.s_addr:=HostToNet(192<<24 or 168<<16 or 210);  if Connect (S,SAddr,Sin,Sout) then  begin   //Make first contact   Reset(Sin);   ReWrite(Sout);   repeat    Readln(SIn,Buffer);    if Buffer<>'' then WriteLn(Buffer);   until Buffer='';   Flush(Sout);   //Get the initial response   repeat    Read(SIn,Buffer);    if Buffer<>'' then WriteLn(Buffer);   until Buffer='';   //Send a query   Reset(Sin);   ReWrite(Sout);   Buffer:='wG40000AU'#$0D#$0A;   Write(Sout,Buffer);   Flush(Sout);   //Get the response   repeat    Read(SIn,Buffer);    if Buffer<>'' then WriteLn(Buffer);   until Buffer='';   Close(sout);  end; end;end.Found here. Need some more research - need to work out how to put a time out on the reading part. ---------------------------------------------------------------------- Thausand · 2025-09-06 10:29 Quote from: geraldholdsworth on September 06, 2025, 11:58:10 amHowever, I found that this works: Is suggest: Not smart is shift network address because depend order. There FPC function for help https://www.freepascal.org/docs- html/rtl/sockets/strtohostaddr.html Have also look below other function and what do. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-06 10:40 Quote from: Thausand on September 06, 2025, 12:29:09 pmIs suggest: Not smart is shift network address because depend order. There FPC function for help https://www.freepascal.org/docs- html/rtl/sockets/strtohostaddr.html Have also look below other function and what do. Cheers - I did find that function afterwards. I just took the code direct from the example and modified it. I've been looking at what other functions the socket unit contains. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-06 12:29 I noticed in that code, which was an exmaple for 'fpConnect' did not feature 'fpConnect', but the depreciated 'Connect'. So, I've played around and got this working: 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 Project1; uses Sockets, SysUtils; Var  SAddr    : TInetSockAddr;  Buffer   : string [255];  input    : String;  S        : Longint;  i,j      : integer; begin S:=fpSocket(AF_INET,SOCK_STREAM,0); if s<>-1 then begin  SAddr.sin_family:=AF_INET;  SAddr.sin_port:=HtoNS(23);  SAddr.sin_addr.s_addr:=HostToNet(192<<24 or 168<<16 or 210);//  SAddr.sin_addr.s_addr:=StrToHostAddr('192.168.0.210').s_addr;  WriteLn('Connecting...');  if fpConnect(S,@SAddr,SizeOf(SAddr))=0 then  begin   //Get the initial response   i:=fpRecv(S,@Buffer,SizeOf(Buffer),0);   if i>0 then   begin    input:='';    for j:=0 to i do input:=input+Buffer[j];    WriteLn(input);   end;   //Send a query   input:='wG40000AU'#$0D#$0A;   StrPCopy(@Buffer,input);   fpSend(S,@Buffer,Length(input),0);   //Reset the buffer   StrPCopy(@Buffer,StringOfChar(#0,255));   //And get the response   i:=fpRecv(S,@Buffer,SizeOf(Buffer),0);   if i>0 then   begin    input:='';    for j:=0 to i do input:=input+Buffer[j];    WriteLn(input);   end;  end; end;end.However, note the commented out line - if I uncomment this (and comment the first), it hangs while trying to connect. Any ideas? (incidentally, I haven't done too much working with pointers) ---------------------------------------------------------------------- Wallaby · 2025-09-06 12:44 QuoteHowever, note the commented out line - if I uncomment this (and comment the first), it hangs while trying to connect. Any ideas? Use StrToNetAddr, you need the bytes to be in the network byte order (Big Endian) while StrToHostAddr would return them in the host byte order (Little Endian). ---------------------------------------------------------------------- geraldholdsworth · 2025-09-06 13:26 Perfect. Thank you. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-07 11:04 OK. That was short lived. Got the program communicating absolutely fine as a command line program. But, as soon as I stick the same code into a GUI application (I rewrote it as a class, then used the exact same class, no changes), it then refuses to connect. This is definately Apple's doing... ---------------------------------------------------------------------- Thaddy · 2025-09-07 11:50 Or Pears for that matter, or Strawberries.. Show us what you did? ---------------------------------------------------------------------- geraldholdsworth · 2025-09-07 12:05 This is the code, put into a class and tested succesfully in a console program: 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 unit TCPClient; {$MODE objfpc}{$H+}{$M+} interface uses  Classes,SysUtils,Sockets; type  TTCPClient = class  private   FSocket : LongInt;  public   Constructor Create;   Destructor Destroy; override;   function IsConnected: Boolean;   function GetLastError: String;  published   procedure Connect(IP: String; Port: Integer);   procedure Disconnect;   function Receive: String;   procedure Send(Output: String);   property Connected : Boolean read IsConnected;   property LastError : String read GetLastError;end; implementation { TTCPClient } {--------------------- ----------------------------------------------------------Creates the class----------------------------------------------------------------- --------------}constructor TTCPClient.Create;begin inherited Create; F Socket:=-1;end; {----------------------------------------------------- --------------------------Are we connected to a server?--------------- ----------------------------------------------------------------}funct ion TTCPClient.IsConnected: Boolean;begin Result:=FSocket<>-1;end; {-- ---------------------------------------------------------------------- -------What was the last error reported?------------------------------ -------------------------------------------------}function TTCPClient.GetLastError: String;begin Result:='Unknown error ('+IntToStr(socketerror)+')'; case socketerror of  EsockADDRINUSE      : Result:='Socket address is already in use';  EsockEACCESS        : Result:='Access forbidden';  EsockEBADF          : Result:='Alias: bad file descriptor';  EsockEFAULT         : Result:='An error occurred';  EsockEINTR          : Result:='Operation interrupted';  EsockEINVAL       : Result:='Invalid value specified';  EsockEMFILE         : Result:='Error code ?';  EsockEMSGSIZE       : Result:='Wrong message size';  EsockENOBUFS        : Result:='No buffer space available';  EsockENOTCONN       : Result:='Not connected';  EsockENOTSOCK       : Result:='File descriptor is not a socket';  EsockEPROTONOSUPPORT: Result:='Protocol not supported';  EsockEWOULDBLOCK    : Result:='Operation would block'; end;end; {--------------------------- ----------------------------------------------------Connect to a server, if not already------------------------------------------------ -------------------------------}procedure TTCPClient.Connect(IP: String; Port: Integer);var SAddr : TInetSockAddr;begin //Make sure we're not already connected if not IsConnected then begin  //Create the socket  FSocket:=fpSocket(AF_INET,SOCK_STREAM,0);  //Created OK?  if FSocket<>-1 then  begin   //Set up the server details   SAddr.sin_family:=AF_INET;   SAddr.sin_port:=HtoNS(Port);   SAddr.sin_addr.s_addr:=StrToNetAddr(IP).s_addr;   //And connect - Timeout is 75 seconds   if fpConnect(FSocket,@SAddr,SizeOf(SAddr))=-1 then Disconnect; //If not connected.  end; end;end; {----------------- --------------------------------------------------------------Disconne ct-------------------------------------------------------------------- -----------}procedure TTCPClient.Disconnect;begin CloseSocket(FSocket) ; FSocket:=-1;end; {-------------------------------------------------- -----------------------------Receives a response - no timeouts-------- ---------------------------------------------------------------------- -}function TTCPClient.Receive: String;var Buffer: String[255]; Lcount: Integer; Index : Integer;begin Result:=''; //Make sure we're connected if IsConnected then begin  //Initialise the buffer  StrPCopy(@Buffer,StringOfChar(#0,255));  //Then send it  Lcount:=fpRecv(FSocket,@Buffer,SizeOf(Buffer),0);  //Copy the response back to the result  if Lcount>0 then   for Index:=0 to Lcount do Resul t:=Result+Buffer[Index]; end;end; {----------------------------------- --------------------------------------------Creates the class--------- ---------------------------------------------------------------------- }procedure TTCPClient.Send(Output: String);var Buffer: String[255];begin //Make sure we're connected if IsConnected then begin  //Copy the query into the buffer  StrPCopy(@Buffer,Output);  //Then send it  fpSend(FSocket,@Buffer,Leng th(Output),0); end;end; {--------------------------------------------- ----------------------------------Destroys the class------------------ -------------------------------------------------------------}destruct or TTCPClient.Destroy;begin if IsConnected then CloseSocket(FSocket); inherited Destroy;end; end.Use the same unit in a GUI, and I get error 65 (which I can't find any details of what this is, but I assume it'll be "No route to host"). 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  Client    :=TTCPClient.Create; Client.Connect('192.168.0.210',23); if Client.Connected then Oh, and I've checked the ESET firewall (even turned it off, again), and gone through the Apple Settings (my application is allowed to access local network). Wireshark doesn't flinch with the GUI application, but does with the console program. ---------------------------------------------------------------------- geraldholdsworth · 2025-09-07 14:41 OK, this is interesting reading. As an experiment, I ran the GUI application from Terminal. Lo and behold, it connected (even WireShark noticed). ---------------------------------------------------------------------- geraldholdsworth · 2025-09-07 21:15 Just to finish off, and in case someone is having the same issue in the future: As an experiment, I compiled the project, then ran the binary (not the package) from where it was compiled and it ran without issue - as it was using Terminal as the launching application. Debugging became an issue though, but I found I could but 'WriteLn' statements throughout the progrm which showed up in Terminal. Thank you everyone who helped out here with suggestions. It seems that sticking with Synapse would have worked, if we knew that macOS was blocking things (despite the Settings saying otherwise).