Java Socket精华分享,一步一步实现神卓内网穿透功能(二)

上一篇简单介绍了一下java socket的基础知识,这一篇先来个知识演练,最终的目的是为了实现仿神卓内网穿透的软件,

Java socket技术实现用户通过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地址、目的地服务器端口。

猜你喜欢

转载自blog.csdn.net/u010905359/article/details/89490520