客户端/服务器套接字和IP地址

时间:2017-01-18 19:35:59

标签: sockets delphi

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

我正在编写一个简单的delphi聊天程序(客户端/服务器套接字),我希望将其转换为迷你游戏。该代码适用于localhost,但是当我将服务器或客户端发送给朋友时,客户端无法连接到服务器。 我们俩都在使用路由器。此外,我认为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.

2 个答案:

答案 0 :(得分:2)

  

我们都在使用路由器

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

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

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

如果服务器的路由器从ISP收到动态IP(这是家庭网络中的常见情况),则IP将定期更改。您可以改为使用动态DNS服务(例如DynDNS,No-IP,Dynu,DuckDNS等)来创建静态主机名,解析为当前IP发生的任何情况,而不是在您想要连接时手动查找当前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,它直接连接到。

答案 1 :(得分:0)

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

您需要将应用程序在ISP路由器上使用的端口转发到本地IP地址才能使其正常工作。 Google&#34;端口转发&#34;开始。

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