I tried making a simple chat system in Delphi 10.3.3 Community Edition using FireMonkey and the Indy components TIdTCPClient
and TIdTCPServer
.
It works fine if the Client and Server are located in the same WiFi network (Server = Windows 10 and Client = Android 10). I used the computer's IPv4 address shown in ipconfig
to get the computer's IP, and my mobile phone successfully connects.
But, if I use the internet IP (got it from https://www.whatismyip.com/de/), the client shows the german equivalent to "socket error # 111 connection refused", so what am I missing? I entered the IP in the edit box to connect to - so if the local IP works, why doesn't any other IP work as well?
Here is the code I used:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
FMX.Controls.Presentation, IdCustomTCPServer, IdTCPServer, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdContext, FMX.ScrollBox, FMX.Memo,
FMX.Edit;
type
TForm1 = class(TForm)
GroupBox1: TGroupBox;
GroupBox2: TGroupBox;
Button1: TButton;
IdTCPClient1: TIdTCPClient;
IdTCPServer1: TIdTCPServer;
Edit1: TEdit;
Edit2: TEdit;
Memo1: TMemo;
Edit3: TEdit;
procedure Button1Click(Sender: TObject);
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure IdTCPServer1Execute(AContext: TIdContext);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
begin
IdTCPClient1.Host := Edit1.Text;
IdTCPClient1.Port := StrToInt(Edit2.Text);
IdTCPClient1.Connect;
if IdTCPClient1.Connected then
begin
IdTCPClient1.IOHandler.WriteLn(Edit3.Text);
IdTCPClient1.Disconnect;
end;
end;
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
var
ip:String;
begin
ip:=AContext.Binding.PeerIP;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('connect: ' + ip)
end);
end;
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
var
ip:String;
begin
ip:=AContext.Binding.PeerIP;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('disconnect: ' + ip)
end);
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
msg:String;
begin
msg:=AContext.Connection.IOHandler.ReadLn;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add('message: ' + msg)
end);
end;
end.
First off, TIdTCPServer
is a multi-threaded component. Its events are fired in the context of worker threads. As such, you must synchronize with the main UI thread when accessing UI controls, like your TMemo
. Without that synchronization, bad things can happen, including but not limited to: crashes, deadlocks, corrupting UI controls, making the UI unresponsive to the user, etc.
That being said, the TIdTCPServer.Bindings
collection can bind listening sockets only to local IPs that belong to the PC that TIdTCPServer
is running on.
If the PC is connected directly to the Internet modem, then the modem's public Internet IP is assigned directly to the PC, so TIdTCPServer
will be able to bind to the Internet IP.
However, if the PC is connected to a LAN network instead (via Ethernet or WiFi), then TIdTCPServer
cannot bind directly to the Internet IP, only to the PC's LAN IP. As such, you will have to setup port forwarding on the network router (via the router's administration site/app, or through uPNP if enabled) to forward inbound traffic from the router's WAN IP/Port to the Server PC's LAN IP/Port. Then clients can connect to the rouer's Internet IP and be forwarded to TIdTCPServer
.