花生壳和神卓互联的NAT产品为什么没有其它公司做出来(分析很到位)

5 篇文章 0 订阅

最近运行了2年的个人网站,即将到期了,网站用户量不大,想着使用公司的工作机子做服务器勉强玩一玩撑一撑好了,但是需要搞一个NAT穿透工具。于是走上了一个对比之路。。

最近因为接了一个项目,完成的差不多了,需要给用户进行演示,就想着把自己的电脑当做服务器用一阵子,但要解决的是我的电脑都是在内网,没有一个公网,这就需要一个内网穿透工具,于是发现市面上一般有这四款比较出名。其中商业化运营的比较成功的是花生壳和神卓互联,前者限制流量,后者不限制可以永久使用,都比较成熟的两款,很多企业项目在用,还有的就是开源的,不能进行商业化运作,如ngrok和FRP。

其中ngrok可能有的人会比较熟悉,最大的缺点就是网速慢和不支持自定义域名,次开关机后,需要手动启动。比较麻烦。而FRP也用过了,自己的观点是需要一台有外网IP的服务器(这是我绝对不想的,我就是不想再买个服务器啊)用时,需要配置好服务器端、和C端。然后各自启动。还有更坑的,若是使用国内的服务器,指向的域名还必须是备案的。。不然会被服务商封掉。

当然ngrok和FRP这里就不多数了,毕竟用的人不多,没有什么社区,今天重点讲的是一般商业化的东西市面上都会有很多企业在竞争,为什么国内只有花生壳和神卓互联两家公司,其中最核心的技术难点在哪?

注意,这里所说的内网穿透技术不是动态域名解析,那个没啥用,国内运营商一般都是限制使用80和443端口,再怎么解析也没有用,那有人说我不用80端口还不行嘛,可以呀,但是你的应用必须要在一级路由下,多级路由想都别想,当然,有的人说了那我换到一级路由下还不行嘛,呵呵,那也没有用,因为你的IP还不是绝对的公网IP,国内运营商IP资源稀缺,大部分家庭和企业的带宽都是运营商的虚拟局域网,我们用的IP都是内网的,这个时候动态域名解析就没有用,所以要想外网访问内网,只有使用内网穿透技术了。

接下来说明一下原理,

这里用Java socket技术实现。因为Java里有nio,用户通过TCP访问服务器 A,服务器 A 再把 TCP 请求转发给服务器 B;同时服务器 A 把服务器 B 返回的数据,转发给用户。也就是服务器 A 作为中转站,在用户和服务器 B 之间转发数据。

神卓互联

其中,服务器A作为用户和服务器B之间的中转站,转发TCP的数据。

服务器B是用户真正访问的服务器,用来接收用户的请求,并且可以向用户发送数据。

先写一个Main.java

 
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
 
public class Main {
    /**
     * 当前服务器ServerSocket的最大连接数
     */
    private static final int MAX_CONNECTION_NUM = 50;
 
    public static void main(String[] args) {
        // 启动一个新线程。检查是否要种植程序。
        new Thread(new CheckRunnable()).start();
 
        // 当前服务器的IP地址和端口号。
        String thisIp = args[0];
        int thisPort = Integer.parseInt(args[1]);
 
        // 转出去的目标服务器IP地址和端口号。
        String outIp = args[2];
        int outPort = Integer.parseInt(args[3]);
 
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(thisPort, MAX_CONNECTION_NUM, InetAddress.getByName(thisIp));
 
            while(true){
                // 用户连接到当前服务器的socket
                Socket s = ss.accept();
 
                // 当前服务器连接到目的地服务器的socket。
                Socket client = new Socket(outIp, outPort);
 
                // 读取用户发来的流,然后转发到目的地服务器。
                new Thread(new ReadWriteRunnable(s, client)).start();
 
                // 读取目的地服务器的发过来的流,然后转发给用户。
                new Thread(new ReadWriteRunnable(client, s)).start();
 
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {         
            try {
                if (null != ss) {
                    ss.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
 
}

ReadWriteRunnable 类。创建对象的时候接受两个 Socket 作为成员变量。从一个 Socket 中读取数据,然后发送到另一个 Socket。

 
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
 
public class ReadWriteRunnable implements Runnable {
 
    /**
     * 读入流的数据的套接字。
     */
    private Socket readSocket; 
 
    /**
     * 输出数据的套接字。
     */
    private Socket writeSocket;
 
    /**
     * 两个套接字参数分别用来读数据和写数据。这个方法仅仅保存套接字的引用,
     * 在运行线程的时候会用到。
     * @param readSocket 读取数据的套接字。
     * @param writeSocket 输出数据的套接字。
     */
    public ReadWriteRunnable(Socket readSocket, Socket writeSocket) {
        this.readSocket = readSocket;
        this.writeSocket = writeSocket;
    }
 
    @Override
    public void run() {
        byte[] b = new byte[1024];   
        InputStream is = null;
        OutputStream os = null;
        try {
            is = readSocket.getInputStream();
            os = writeSocket.getOutputStream();
            while(!readSocket.isClosed() && !writeSocket.isClosed()){
                int size = is.read(b); 
                if (size > -1) {
                    os.write(b, 0, size);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (null != os) {
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }   
        }
 
    }
 
}

在命令行执行这个程序的时候,需要输入四个参数。分别是当前服务器IP地址、当前服务器端口、目的地服务器IP地址、目的地服务器端口。

由于是入门的,所以这里就用socket技术解释了下,一般商业化的产品都是用多路复用技术,不是简单的socket就能支撑的。以下是网站的截图,可以看到都是支持多映射绑定,每条映射都必须有断线重连机制,这个不是很难,难得是流量大的时候要保持稳定,毕竟做ToC最需要的是稳定,后来自己努力做了一个内网好像已经放github上了,

神卓的

花生壳的

看到评论有说怎么连接到内网,其实通道建立之后就已经可以连接了,要把模式设置成非阻塞,不然速度会很慢。

 

 

 

 

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值