p2p也就是点对点通讯技术,如果熟悉网络协议的话,p2p的原理是很简单的。内网的电脑要访问外网
必须要经过路由器,路由器会为该电脑分配一个端口,有了这个端口之后,就可以访问外网的电脑。
外网的电脑也是通过这个端口访问这台电脑的,但相对于内网访问外网,外网访问内网是比较困难的,
因为路由器只会让经过认证的ip通过这个端口,p2p的作用就是就在于此。
我们可以在外网放一台服务器,服务器上记录了来自不通网段的ip和端口。当内网的A电脑要与B
一台电脑通讯,我们可以向服务器发送请求,让服务器通知B电脑,A电脑要与你通讯,请把他的ip
void InitWinSock()
{
vector<User*> users;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET PrimaryUDP=socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in local;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_family=AF_INET;
local.sin_port=htons(3650);
int nResult=bind(PrimaryUDP,(const sockaddr*)&local,sizeof(sockaddr));
sockaddr_in sender;
User *recvbuf=new User();
for(;;)
{
int dwSender = sizeof(sender);
int ret = recvfrom(PrimaryUDP, (char *)recvbuf, sizeof(User), 0, (sockaddr *)&sender, &dwSender);
if(recvbuf->cmd==0)
{
User *userip=new User();
userip->ip=sender.sin_addr.s_addr;
userip->port=sender.sin_port;
users.push_back(userip);
}
else if(recvbuf->cmd==1)
{
for(vector<User*>::iterator it=users.begin();it!=users.end();it++)
{
if((*it)->port==sender.sin_port)
continue;
(*it)->cmd=1;
sendto(PrimaryUDP,(const char*)(*it),sizeof(User),0,(const sockaddr*)(&sender),sizeof(sender));
break;
}
}
else if(recvbuf->cmd==2)
{
//解出接收方的ip信息
sockaddr_in remote;
remote.sin_family=AF_INET;
remote.sin_port= recvbuf->port;//直接使用网络字节
remote.sin_addr.S_un.S_addr = recvbuf->ip;
//创建发送者的ip信息
User thisuser;
memset(&thisuser,0,sizeof(User));
thisuser.ip=sender.sin_addr.S_un.S_addr;//直接使用网络字节
thisuser.port=sender.sin_port;
thisuser.cmd=2;
//Hole,关键代码
sendto(PrimaryUDP,(const char*)&thisuser, sizeof(User), 0, (const sockaddr *)&remote, sizeof(remote));
}
}
delete recvbuf;
{
InitWinSock();
return 0;
TUser = packed record
ip:uint;
port:uint;
cmd:byte;//0:注册ip 1:取得对方ip 2: 打洞 3:p2p通讯 4:什么也不做
end;
var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=ip;
user.port:=port;
user.cmd:=2;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
var
wsaData:TWSADATA;
begin
WSAStartup(MakeWord(2,2), wsaData);
PrimaryUDP:=socket(AF_INET, SOCK_DGRAM, 0);
remote.sin_addr.S_addr:=inet_addr('192.168.0.6');
remote.sin_port:=htons(3650);
remote.sin_family:=AF_INET;
TRecvThread.Create(false);
var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=0;
user.port:=0;
user.cmd:=0;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=0;
user.port:=0;
user.cmd:=1;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
procedure TRecvThread.DoShowMessage();
begin
ShowMessage('hi!I`m duck!');
var
user:PUser;
len:integer;
remoteClient:sockaddr_in;
FDSet:TFDSet;
begin
FD_ZERO(FDSet);
FD_SET(PrimaryUDP, FDSet);
GetMem(user,sizeof(Tuser));
len:=sizeof(remote);
while true do
begin
if select(0,@FDSet, nil, nil, nil)>0 then
begin
recvfrom(PrimaryUDP,user^,sizeof(TUser),0,remote,len);
if user.cmd=1 then
begin
ip:=User.ip;
port:=User.port;
end
else if user.cmd=2 then//开始向路由器打洞
begin
remoteClient.sin_family:=AF_INET;//remoteClient是另一台电脑在外网的ip
remoteClient.sin_port:=User.port;
remoteClient.sin_addr.S_addr:=User.ip;
User.cmd:=4;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remoteClient,sizeof(remoteClient));
end
else if user.cmd=3 then
begin
Synchronize(DoShowMessage);
end;
end;
end;
FreeMem(user);
必须要经过路由器,路由器会为该电脑分配一个端口,有了这个端口之后,就可以访问外网的电脑。
外网的电脑也是通过这个端口访问这台电脑的,但相对于内网访问外网,外网访问内网是比较困难的,
因为路由器只会让经过认证的ip通过这个端口,p2p的作用就是就在于此。
我们可以在外网放一台服务器,服务器上记录了来自不通网段的ip和端口。当内网的A电脑要与B
一台电脑通讯,我们可以向服务器发送请求,让服务器通知B电脑,A电脑要与你通讯,请把他的ip
放到路由器上去,这样就可以了。
服务器:
void InitWinSock()
{
vector<User*> users;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET PrimaryUDP=socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in local;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_family=AF_INET;
local.sin_port=htons(3650);
int nResult=bind(PrimaryUDP,(const sockaddr*)&local,sizeof(sockaddr));
sockaddr_in sender;
User *recvbuf=new User();
for(;;)
{
int dwSender = sizeof(sender);
int ret = recvfrom(PrimaryUDP, (char *)recvbuf, sizeof(User), 0, (sockaddr *)&sender, &dwSender);
if(recvbuf->cmd==0)
{
User *userip=new User();
userip->ip=sender.sin_addr.s_addr;
userip->port=sender.sin_port;
users.push_back(userip);
}
else if(recvbuf->cmd==1)
{
for(vector<User*>::iterator it=users.begin();it!=users.end();it++)
{
if((*it)->port==sender.sin_port)
continue;
(*it)->cmd=1;
sendto(PrimaryUDP,(const char*)(*it),sizeof(User),0,(const sockaddr*)(&sender),sizeof(sender));
break;
}
}
else if(recvbuf->cmd==2)
{
//解出接收方的ip信息
sockaddr_in remote;
remote.sin_family=AF_INET;
remote.sin_port= recvbuf->port;//直接使用网络字节
remote.sin_addr.S_un.S_addr = recvbuf->ip;
//创建发送者的ip信息
User thisuser;
memset(&thisuser,0,sizeof(User));
thisuser.ip=sender.sin_addr.S_un.S_addr;//直接使用网络字节
thisuser.port=sender.sin_port;
thisuser.cmd=2;
//Hole,关键代码
sendto(PrimaryUDP,(const char*)&thisuser, sizeof(User), 0, (const sockaddr *)&remote, sizeof(remote));
}
}
delete recvbuf;
}
int main()
{
InitWinSock();
return 0;
}
TUser = packed record
ip:uint;
port:uint;
cmd:byte;//0:注册ip 1:取得对方ip 2: 打洞 3:p2p通讯 4:什么也不做
end;
puser=^TUser;
procedure TForm1.Button1Click(Sender: TObject);//打洞var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=ip;
user.port:=port;
user.cmd:=2;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
end;
var
wsaData:TWSADATA;
begin
WSAStartup(MakeWord(2,2), wsaData);
PrimaryUDP:=socket(AF_INET, SOCK_DGRAM, 0);
remote.sin_addr.S_addr:=inet_addr('192.168.0.6');
remote.sin_port:=htons(3650);
remote.sin_family:=AF_INET;
TRecvThread.Create(false);
end;
var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=0;
user.port:=0;
user.cmd:=0;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
end;
procedure TForm1.Button4Click(Sender: TObject);//取得对方ipvar
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=0;
user.port:=0;
user.cmd:=1;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));
FreeMem(user);
end;
procedure TForm1.Button3Click(Sender: TObject);//p2p通讯
var
user:PUser;
begin
GetMem(user,sizeof(Tuser));
user.ip:=ip;
user.port:=port;
user.cmd:=3;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remote,sizeof(remote));//注意,经过打洞后,remote的地址和端口已经发生变化,不再是指向服务器了,而是指向另一端的电脑。
FreeMem(user);
end;
procedure TRecvThread.DoShowMessage();
begin
ShowMessage('hi!I`m duck!');
end;
var
user:PUser;
len:integer;
remoteClient:sockaddr_in;
FDSet:TFDSet;
begin
FD_ZERO(FDSet);
FD_SET(PrimaryUDP, FDSet);
GetMem(user,sizeof(Tuser));
len:=sizeof(remote);
while true do
begin
if select(0,@FDSet, nil, nil, nil)>0 then
begin
recvfrom(PrimaryUDP,user^,sizeof(TUser),0,remote,len);
if user.cmd=1 then
begin
ip:=User.ip;
port:=User.port;
end
else if user.cmd=2 then//开始向路由器打洞
begin
remoteClient.sin_family:=AF_INET;//remoteClient是另一台电脑在外网的ip
remoteClient.sin_port:=User.port;
remoteClient.sin_addr.S_addr:=User.ip;
User.cmd:=4;
sendto(PrimaryUDP,user^,sizeof(TUser),0,remoteClient,sizeof(remoteClient));
end
else if user.cmd=3 then
begin
Synchronize(DoShowMessage);
end;
end;
end;
FreeMem(user);
end;