UDP打洞代码记录(内网到外网打洞)

前几天写的又忘了怎么做了,还是老老实实记录下来吧.

先从网上拔下一段话:

NAT大致分为下面四类
1) Full Cone
这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口的UDP数据报都可以到达A.不管是不是C发过来的.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)

2) Restricted Cone
这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用任何端口和A通信.其他的外网机器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何从C发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)

3) Port Restricted Cone
这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用原来的端口和A通信.其他的外网机器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
C(202.88.88.88:2000)发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)
以上三种NAT通称Cone NAT.我们只能用这种NAT进行UDP打洞.

4) Symmetic
对于这种NAT.连接不同的外部目标.原来NAT打开的端口会变化.而Cone NAT不会.虽然可以用端口猜测.但是成功的概率很小.因此放弃这种NAT的UDP打洞.

已经分不清谁是原作者了,如果原作者看到请留言.我会附上转载说明

综上所述:

1.Full Cone  : 最随意  ,  程序打开一个端口后所有机器的所有程序都可以通过发给这个端口到达程序.(没有限制)

2.Restricted Cone :  相比于Full,不允许其他机器访问.(只限制机器)

3.Port Restricted Cone : 除了不允许其他机器访问以外,对于源机器来说不允许其他端口访问自己.(限制机器及其端口)

4.Symmetic : 连接不同目标端口不同.

 

一.公网recver和 内网sender打洞.

由内网sender向recver发送udp报文,由于recver在公网所以可以接收到此报文.并获取到sender路由器net所开辟的端口. 由于消息由内网sender发送给公网,所以内网路由器信任此公网recver.

之后就可以正常通信了.

但是要注意防止丢包(保证公网可以收到来自内网的报文以获取端口信息).

recver.cpp:

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

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(8001);
    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;
    }

    struct sockaddr_in ca[2];
    memset(&ca, 0, sizeof(ca));
    char buff[100];
    socklen_t slen;

    int i = 0;
    ssize_t size;
    for(; i < 2; i++)
    {
        size = recvfrom(fd, &buff, sizeof(buff), 0, (struct sockaddr *)&(ca[i]), &slen);
        if( size < 0 )
        {
            printf("recvfrom error: %s\n", strerror(errno));
            return 1;
        }

        printf("recvfrom ip: %s, port: %d\n", inet_ntoa(ca[i].sin_addr), ntohs(ca[i].sin_port));
    }

    size = sendto(fd, &ca[0], sizeof(ca[0]), 0, (struct sockaddr *)&ca[1], sizeof(ca[1]));
    if ( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    size = sendto(fd, &ca[1], sizeof(ca[1]), 0, (struct sockaddr *)&ca[0], sizeof(ca[0]));
    if ( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}

sender.cpp:

#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>
#include <unistd.h>
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(8001);
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");//118.89.231.196

    char *p = "a";

    ssize_t size = sendto(fd, p, 1, 0, (struct sockaddr *)&sa, sizeof(sa));
    if( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    printf("send ok recv now\n");

    char buff[100];
    struct sockaddr_in ca;
    socklen_t slen;
    memset(&ca, 0, sizeof(ca));
    size = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&ca, &slen);
    if( size < 0 )
    {
        printf("recvfrom error: %s\n", strerror(errno));
        return 1;
    }
    struct sockaddr_in *ps = (struct sockaddr_in *)buff;

    printf("recv another client ip: %s, port: %d\n", inet_ntoa(ps->sin_addr), ntohs(ps->sin_port));

    int i;
    for( i = 0; i < 3; i++ )
    {
        printf("send %d times\n", i);
        p = "hello world";
        ps->sin_family = AF_INET;
        size = sendto(fd, p, strlen(p), 0, (struct sockaddr *)ps, sizeof(struct sockaddr_in));
        if( size < 0 )
        {
            printf("sendto error: %s\n", strerror(errno));
            return 1;
        }

        printf("send %d times ok, recv now\n", i);

        char buf[100] = {0};
        struct sockaddr_in ca1;
        memset(&ca1, 0, sizeof(ca1));
        size = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ca1, &slen);
        if( size < 0 )
        {
            printf("recvfrom error: %s\n", strerror(errno));
            return 1;
        }

        printf("recv %d message: %s\n", i, buf);
        sleep(1);
    }

    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值