许多P2P软件比如SKYPE,QQ,电驴之类需要不同内网的两台机子进行通信,而路由器的NAT机制决定了内网访问外网容易,而外网访问内网困难,那如何才能做到这一点呢?有办法------打洞!
具体实现方法需要一台服务器,现在假设两台内网PC,A和B想用端口40000通信,网关分别为NATA,NATB.服务器为S,配置如下:
A: 192.168.0.34 40000
NATA: 58.240.157.121 60020
B: 192.168.0.227 40000
NATB: 58.240.157.222 50030
S: 58.240.157.240 40000
打洞过程:
1.A访问S,打一个洞,洞的指向为A<->S
2.B访问S,打一个洞,洞的指向为B<->S
3.S访问A,告诉它:B想访问你
4.A访问B,洞的指向为A<->B,这个包B的路由器NATB收到后不会转发给B,而是丢弃,因为它认为这是来历不明的包:(
5.B访问A,洞的指向为B<->A,此时A与B可以进行双向通信,打洞成功
打洞的目的是为了告诉NAT,我要访问的IP是我"朋友",你不能阻拦它发过来的信息,比如第4步A通过发送这个包,告诉了NATA:B是我朋友;第5步B发送包给A,告诉了NATB:A是我朋友.最后NATA认识了B,而NATB认识了A,A与B终于实现了双向通信.
外网服务器向内网发送udp数据的demo
外网是不能直接发包给内网的,即便是知道内网路由器的IP和端口也不行,路由器会拦截,通讯过程应该是内网先发送数据包给外网服务器,因为外网服务器直接拥有的是外网IP地址,所以会收到内网计算机发出的数据包,这个时候外网收到后解析出该数据包的来源(IP+端口),然后往这个地址回,内网就能收到了。
需要内网先发包给外网,外网收到后解析出内网的地址,再回发。简单说就是要里应外合,内应先勾搭。
server.c
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#define SERVER_PORT 6666
int main(int argc, const char *argv[])
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if( fd < 0 )
{
printf("socket error: %s\n", strerror(errno));
return 1;
}
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(SERVER_PORT);
sa.sin_addr.s_addr = INADDR_ANY;
int ret = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
if( ret < 0 )
{
printf("bind error: %s\n", strerror(errno));
return 1;
}
char recvline[200];
struct sockaddr_in sender;
int dwSender = sizeof(sender);
int n=recvfrom(fd, recvline, 200, 0, (struct sockaddr *)&sender, &dwSender);
printf("recvfrom recvline : %s\n", recvline);
char sendline[200] = "hello client";
sendto(fd, (const char*)&sendline, sizeof(sendline), 0, (struct sockaddr*)&sender, sizeof(sender));
return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 6666
char serverip[100] = "192.168.209.134";
int main(int argc, const char *argv[])
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if( sock < 0 )
{
printf("socket error: %s\n", strerror(errno));
return 1;
}
struct sockaddr_in sin;
sin.sin_addr.s_addr = htonl(INADDR_ANY) ;
sin.sin_family = AF_INET;
sin.sin_port = 0;
if (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
printf("bind error: %s\n", strerror(errno));
return 1;
}
struct sockaddr_in remote;
remote.sin_addr.s_addr = inet_addr(serverip);
remote.sin_family = AF_INET;
remote.sin_port = htons(SERVER_PORT);
char recvline[200],sendline[200] = "hello";
sendto(sock, (const char*)&sendline, sizeof(sendline), 0, (struct sockaddr*)&remote, sizeof(remote));
int count;
int fromlen = sizeof(remote);
int iread = recvfrom(sock, recvline, 200, 0, (struct sockaddr *)&remote, &fromlen);
printf("recvline : %d\n", recvline);
if(iread<=0)
{
printf("recvfrom error: %s\n", strerror(errno));
return 1;
}
return 0;
}