【问题标题】:TCP/IP Indy Chat Delphi 10.3.3 FMX (using internet, not only LAN)TCP/IP Indy Chat Delphi 10.3.3 FMX(使用互联网,不只是局域网)
【发布时间】:2021-03-07 12:59:00
【问题描述】:

我尝试在 Delphi 10.3.3 Community Edition 中使用 FireMonkey 和 Indy 组件 TIdTCPClientTIdTCPServer 制作一个简单的聊天系统。

如果客户端和服务器位于同一个 WiFi 网络中(服务器 = Windows 10 和客户端 = Android 10),则可以正常工作。我用ipconfig显示的电脑IPv4地址获取电脑IP,手机连接成功。

但是,如果我使用互联网 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.

【问题讨论】:

    标签: delphi tcp firemonkey indy


    【解决方案1】:

    首先,TIdTCPServer 是一个多线程组件。它的事件在工作线程的上下文中触发。因此,在访问 UI 控件时,您必须与主 UI 线程同步,例如您的 TMemo。如果没有这种同步,可能会发生坏事,包括但不限于:崩溃、死锁、破坏 UI 控件、使 UI 对用户无响应等。

    话虽如此,TIdTCPServer.Bindings 集合只能将侦听套接字绑定到属于运行TIdTCPServer 的 PC 的本地 IP。

    如果 PC 直接连接到 Internet 调制解调器,则调制解调器的公共 Internet IP 直接分配给 PC,因此TIdTCPServer 将能够绑定到 Internet IP。

    但是,如果 PC 连接到 LAN 网络(通过以太网或 WiFi),则 TIdTCPServer 无法直接绑定到 Internet IP,只能绑定到 PC 的 LAN IP。因此,您必须在网络路由器上设置端口转发(通过路由器的管理站点/应用程序,或通过 uPNP,如果启用)将入站流量从路由器的 WAN IP/端口转发到服务器PC 的 LAN IP/端口。然后客户端可以连接到路由器的Internet IP并转发到TIdTCPServer

    【讨论】:

    • 谢谢。我想这是解决方案,但是尽管我将路由器配置为允许在我的设备上进行端口转发,但它仍然无法连接并像以前一样显示错误 #111。
    • @qGold 那么您没有正确设置端口转发或侦听服务器。请提供服务器正在侦听的实际 IP/端口对,以及您为端口转发设置的实际 WAN IP/端口 -> LAN IP/端口映射。
    • 现在我在上面的代码中添加了同步线程。这看起来正确吗?它可以正常工作,现在没有任何错误或 UI 损坏。
    • 有没有办法让我获得客户端服务器连接,而无需在路由器上进行端口转发? (实际上我可以修改路由器的设置,但如果能够让任何用户充当客户端和服务器来交换数据而无需编辑路由器配置,那就太好了)
    • 这看起来正确吗?” - 差不多。不要在同步代码中执行AContext 操作。在调用线程中执行它们,并将结果保存到匿名过程可以捕获的局部变量中,例如:var s: string; s := AContext.Binding.PeerIP; TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('connect: ' + s); end); ... var s: string; s := AContext.Connection.IOHandler.ReadLn; TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('message: ' + s); end);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-30
    • 2012-06-08
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2012-09-13
    相关资源
    最近更新 更多