【问题标题】:Client/Server sockets and ip addresses客户端/服务器套接字和 IP 地址
【发布时间】:2017-01-18 19:35:59
【问题描述】:

如果你们中的一些人有时间和耐心帮助我解决我的问题,我将不胜感激

我正在编写一个简单的 delphi 聊天程序(客户端/服务器套接字),我想将其转换为一个迷你游戏。该代码适用于本地主机,但是当我将服务器或客户端发送给朋友时,客户端无法连接到服务器。 我们俩都在使用路由器。此外,我认为 ISP 为我们提供 IP 的方式有些问题。我的意思是:“ipconfig”显示我有 192.168.x.x ;但是像“whatismyip.com”这样的网站向我显示了一个不同的地址。而且我不使用任何VPN。函数 TForm1.GetIPAddress 显示 192.168.x.x 之一。

那么,问题是:我应该如何编写聊天程序?鉴于上述连接问题,我必须使用哪些选项来连接服务器和客户端?

这是服务器的代码:

unit Unit1;

interface

uses
  Windows, Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Win.ScktComp,
  Vcl.ExtCtrls, winsock;

type
  TForm1 = class(TForm)
    server: TServerSocket;
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    Label3: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Panel1: TPanel;
    Panel2: TPanel;
    Button3: TButton;
    Edit3: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure serverClientConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure serverClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    Function GetIPAddress:String;
    procedure serverClientRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  server.Port:=strtoint(edit2.Text);
  server.Active:=true;
  Memo1.Lines.Add('server started');
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
server.Active:=false;
memo1.Lines.Add('server stopped');
end;


procedure TForm1.Button3Click(Sender: TObject);
var
  i: integer;
  Str:String;
begin
     Str:=Edit3.Text;
     Memo1.Lines.Add('server: '+Str);
     Edit3.Text:='';
     for i:=0 to server.Socket.ActiveConnections-1 do
      server.Socket.Connections[i].SendText(str);
end;


procedure TForm1.FormShow(Sender: TObject);
begin
edit1.Text:=getipaddress;
end;


procedure TForm1.serverClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
   memo1.Lines.Add('client connected '+socket.RemoteAddress);

end;

procedure TForm1.serverClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
   memo1.Lines.Add('client disconnected'+socket.RemoteAddress);
end;


procedure TForm1.serverClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
    memo1.Lines.Add('client: '+socket.ReceiveText);
end;


Function TForm1.GetIPAddress:String;
type
  pu_long = ^u_long;
var
  varTWSAData : TWSAData;
  varPHostEnt : PHostEnt;
  varTInAddr : TInAddr;
  namebuf : Array[0..255] of AnsiChar;
begin
  If WSAStartup($101,varTWSAData) <> 0 Then
  Result := 'No. IP Address'
  Else Begin
    gethostname(namebuf,sizeof(namebuf));
    varPHostEnt := gethostbyname(namebuf);
    varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
    Result := inet_ntoa(varTInAddr);
  End;
  WSACleanup;
end;


end.

对于客户来说:

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  System.Win.ScktComp, Vcl.StdCtrls;

type
  TForm2 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    client: TClientSocket;
    Label1: TLabel;
    Label2: TLabel;
    Button3: TButton;
    Edit3: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure clientConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure Button2Click(Sender: TObject);
    procedure clientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure clientRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
client.Address:=edit1.Text;
client.Port:=strtoint(edit2.Text);
client.Active:=true;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
client.Active:=false;
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
  if client.Active then  begin
      client.Socket.SendText(edit3.Text) ;
       memo1.Lines.Add('client :'+edit3.Text);
       edit3.Text:='';
  end
  else showmessage('not connected');
end;

procedure TForm2.clientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  memo1.Lines.Add('connected') ;
end;

procedure TForm2.clientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
     memo1.Lines.Add('disconnected') ;
end;

procedure TForm2.clientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
    memo1.Lines.Add('server: '+socket.ReceiveText)
end;

end.

【问题讨论】:

  • 您的问题与 Delphi 几乎没有关系,真的。您需要使用外部/WAN IP 地址(“whatsmyip”为您提供的地址)来连接和配置路由器以进行端口转发。把你的问题分解成碎片:首先,让你的程序单独在你的机器上运行,客户端和服务器(这是你问题的 Delphi 部分);然后您将学习如何在路由器上转发端口。
  • 这不是真正的编码问题,而是网络问题,所以它属于ServerFault而不是StackOverflow。
  • 我明白了,谢谢大家。我可以在 hamachi 之类的 VPN 的帮助下使用服务器/客户端吗?
  • 客户端/服务器在任何 TCP/IP 连接上都可以正常工作,无论是否是 VPN。重要的是您了解客户端和服务器之间的网络连接布局,以便客户端可以连接到正确的主机名/IP。如果两台机器在同一个 LAN 上,即使通过 VPN,客户端也可以连接到服务器的 LAN IP。否则,客户端必须通过 Internet 连接到服务器的 ISP 分配的公共 IP。

标签: sockets delphi


【解决方案1】:

我们都在使用路由器

您必须在服务器的路由器上配置端口转发规则。

通过服务器端路由器的入站连接必须从路由器的公共 WAN IP/端口转发到服务器计算机的专用 LAN IP/端口。然后外部的出站客户端可以连接到服务器路由器的公共 IP/端口:

client -> internet -> router (104.34.x.x:YYYY) -> server (192.168.x.x:ZZZZ)

如果服务器的路由器从 ISP 接收到动态 IP(这是家庭网络中的常见情况),则 IP 将定期更改。与其在想要连接时手动查找当前 IP,不如使用动态 DNS 服务(例如 DynDNS、No-IP、Dynu、DuckDNS 等)创建一个静态主机名,该主机名可以解析为当前 IP 发生的任何内容是(大多数路由器都有一个内置选项,可以在路由器的 IP 更改时自动更新此类服务)。然后客户端可以直接连接到主机名而不是路由器的 IP:

client.Host := 'myDynDNShostname';

client -> DNS (what IP is "myDynDNShostname"?) -> "104.34.x.x"
then
client -> internet -> router (104.34.x.x) -> server (192.168.x.x)

即使 ISP 分配的是静态 IP 而不是动态 IP,考虑为其创建静态主机名仍然是个好主意,因为主机名往往比 IP 地址更容易记住。

另外,我认为 ISP 向我们提供 IP 的方式也有问题。我的意思是:“ipconfig”显示我有 192.168.x.x ;但是像“whatismyip.com”这样的网站向我显示了一个不同的地址。

这是完全正常的。 192.168.x.x 是专用 LAN IP。在服务器机器上运行ipconfig 将显示路由器分配给机器的IP,而不是ISP 分配的路由器公共IP。 “whatismyip.com”之类的站点只能显示直接连接到它们的远程 IP,在这种情况下是路由器的公共 IP,而不是服务器的私有 LAN IP:

browser (192.168.x.x) -> router (104.34.x.x) -> internet -> website

函数 TForm1.GetIPAddress 显示 192.168.x.x 之一。

因为这是直接连接到的路由器分配给该本地计算机的 IP。

【讨论】:

    【解决方案2】:

    您的朋友应该尝试连接到 whatismyip.com 等网站向您显示的 IP 地址。 192.168.x.x 是您家庭网络上的私有 IP 地址,无法直接从 Internet 访问。

    您需要将您的应用程序在您的 ISP 路由器上使用的端口转发到您的本地 IP 地址以使这项工作正常进行。谷歌“端口转发”开始。

    请注意,您可能会从 ISP 收到动态 IP 地址,这意味着该 IP 会定期更改。因此,您需要检查您的 IP 是什么,并在连接前向您的朋友提供更新的地址。另一种方法是使用动态 DNS 服务,这将允许您的朋友使用域名而不是 IP 地址连接到您的应用程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-10
      • 2018-11-25
      • 1970-01-01
      • 2013-11-11
      • 1970-01-01
      相关资源
      最近更新 更多