UDP打洞技术与应用

许多P2P软件比如SKYPE,QQ,电驴之类需要不同内网的两台机子进行通信,而路由器的NAT机制决定了内网访问外网容易,而外网访问内网困难,那如何才能做到这一点呢?有办法------打洞!

 

具体实现方法需要一台服务器,现在假设两台内网PC,AB想用端口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,此时AB可以进行双向通信,打洞成功

 

打洞的目的是为了告诉NAT,我要访问的IP是我"朋友",你不能阻拦它发过来的信息,比如第4A通过发送这个包,告诉了NATA:B是我朋友;5B发送包给A,告诉了NATB:A是我朋友.最后NATA认识了B,NATB认识了A,AB终于实现了双向通信.

外网服务器向内网发送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;
}


client.c


#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;
}


猜你喜欢

转载自blog.csdn.net/xuqiqiang1993/article/details/67632484