TCP/IP Indy Chat Delphi 10.3.3 FMX(使用互联网,不仅是局域网)
TCP/IP Indy Chat Delphi 10.3.3 FMX (using internet, not only LAN)
我尝试使用 FireMonkey 和 Indy 组件 TIdTCPClient
和 TIdTCPServer
.
在 Delphi 10.3.3 社区版中制作一个简单的聊天系统
如果客户端和服务器位于同一个 WiFi 网络(服务器 = Windows 10 和客户端 = Android 10),则工作正常。我使用ipconfig
中显示的电脑IPv4地址获取电脑IP,手机phone成功连接
但是,如果我使用互联网 IP(从 https://www.whatismyip.com/de/ 获取),客户端会显示相当于“套接字错误 #111 连接被拒绝”的德语,那么我错过了什么?我在编辑框中输入了要连接的 IP - 如果本地 IP 有效,为什么其他 IP 也无效?
这是我使用的代码:
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.
首先,TIdTCPServer
是一个多线程组件。它的事件在工作线程的上下文中被触发。因此,在访问 UI 控件时,您必须与主 UI 线程同步,例如您的 TMemo
。如果没有这种同步,就会发生不好的事情,包括但不限于:崩溃、死锁、损坏 UI 控件、使 UI 对用户无响应等。
也就是说,TIdTCPServer.Bindings
集合只能将侦听套接字绑定到属于 TIdTCPServer
运行 的 PC 的本地 IP。
如果 PC 直接连接到 Internet 调制解调器,则调制解调器的 public Internet IP 直接分配给 PC,因此 TIdTCPServer
将能够绑定到 Internet IP。
但是,如果 PC 连接到 LAN 网络(通过以太网或 WiFi),则 TIdTCPServer
无法直接绑定到 Internet IP,只能绑定到 PC 的 LAN IP。因此,您必须在网络路由器上设置 端口转发(通过路由器的管理 site/app,或通过 uPNP,如果启用)转发来自路由器 WAN 的入站流量 IP/Port 到服务器 PC 的 LAN IP/Port。然后客户端可以连接到 rouer 的 Internet IP 并转发到 TIdTCPServer
.
我尝试使用 FireMonkey 和 Indy 组件 TIdTCPClient
和 TIdTCPServer
.
如果客户端和服务器位于同一个 WiFi 网络(服务器 = Windows 10 和客户端 = Android 10),则工作正常。我使用ipconfig
中显示的电脑IPv4地址获取电脑IP,手机phone成功连接
但是,如果我使用互联网 IP(从 https://www.whatismyip.com/de/ 获取),客户端会显示相当于“套接字错误 #111 连接被拒绝”的德语,那么我错过了什么?我在编辑框中输入了要连接的 IP - 如果本地 IP 有效,为什么其他 IP 也无效?
这是我使用的代码:
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.
首先,TIdTCPServer
是一个多线程组件。它的事件在工作线程的上下文中被触发。因此,在访问 UI 控件时,您必须与主 UI 线程同步,例如您的 TMemo
。如果没有这种同步,就会发生不好的事情,包括但不限于:崩溃、死锁、损坏 UI 控件、使 UI 对用户无响应等。
也就是说,TIdTCPServer.Bindings
集合只能将侦听套接字绑定到属于 TIdTCPServer
运行 的 PC 的本地 IP。
如果 PC 直接连接到 Internet 调制解调器,则调制解调器的 public Internet IP 直接分配给 PC,因此 TIdTCPServer
将能够绑定到 Internet IP。
但是,如果 PC 连接到 LAN 网络(通过以太网或 WiFi),则 TIdTCPServer
无法直接绑定到 Internet IP,只能绑定到 PC 的 LAN IP。因此,您必须在网络路由器上设置 端口转发(通过路由器的管理 site/app,或通过 uPNP,如果启用)转发来自路由器 WAN 的入站流量 IP/Port 到服务器 PC 的 LAN IP/Port。然后客户端可以连接到 rouer 的 Internet IP 并转发到 TIdTCPServer
.