Java實現端口映射/轉發


20190630 14:57更新

添加了一個功能 檢查當前連接狀態 同時發現清除超時連接忘了加定時器 現在清除超時連接可以正常工作了

  1 import java.io.IOException;
  2 import java.io.InputStream;
  3 import java.io.OutputStream;
  4 import java.net.ServerSocket;
  5 import java.net.Socket;
  6 import java.text.SimpleDateFormat;
  7 import java.util.*;
  8 
  9 public class SimpleForward implements Runnable {
 10     // 服務器
 11     private ServerSocket server;
 12     // 監聽本地端口
 13     private int localPort = 1080;
 14     // 目標主機地址
 15     private String remoteHostAddr = "0.0.0.0";
 16     // 目標主機端口
 17     private int remoteHostPort = 8388;
 18     // 設置超時時間 30s
 19     private static int TIMEOUT = 30;
 20     // 客戶端列表 用於刪除失效連接和超時連接
 21     private static HashMap<Socket, Date> clientList = new HashMap<>();
 22 
 23     public static void main(String[] args) throws IOException {
 24 //        new SimpleForward();
 25         new SimpleForward(1234, "127.0.0.1", 1080);
 26     }
 27 
 28     public SimpleForward() throws IOException {
 29         run();
 30     }
 31 
 32     public SimpleForward(int localPort, String remoteHostAddr, int remoteHostPort) throws IOException {
 33         this.localPort = localPort;
 34         this.remoteHostAddr = remoteHostAddr;
 35         this.remoteHostPort = remoteHostPort;
 36         run();
 37     }
 38 
 39     @Override
 40     public void run() {
 41         try {
 42             this.server = new ServerSocket(this.localPort);
 43             System.out.println("服務器開啟成功");
 44             System.out.println("監聽端口 : " + this.localPort);
 45         } catch (IOException e) {
 46             System.out.println("服務器開啟失敗");
 47             System.out.println(e.getMessage());
 48             System.out.println("退出運行");
 49             return;
 50         }
 51         // 自動清除失效連接和超時連接
 52         new Thread(new Terminal()).start();
 53         new Thread(new AutoDestroy()).start();
 54         while (true) {
 55             Socket socket = null;
 56             Socket remoteHost = null;
 57             try {
 58                 socket = server.accept();
 59                 // 接收到請求就把socket扔進map,value為刷新時間
 60                 clientList.put(socket, new Date());
 61                 String address = socket.getRemoteSocketAddress().toString();
 62                 System.out.println("新連接 : " + address);
 63                 // 建立與目標主機的連接
 64                 remoteHost = new Socket(this.remoteHostAddr, this.remoteHostPort);
 65                 System.out.println("連接地址 : " + this.remoteHostAddr + ":" + this.remoteHostPort);
 66                 // 端口轉發
 67                 new Thread(new Switch(socket, remoteHost, remoteHost.getInputStream(), socket.getOutputStream())).start();
 68                 new Thread(new Switch(socket, remoteHost, socket.getInputStream(), remoteHost.getOutputStream())).start();
 69             } catch (IOException e) {
 70                 System.out.println("連接異常");
 71                 System.out.println(e.getMessage());
 72                 close(socket);
 73                 close(remoteHost);
 74             }
 75         }
 76     }
 77 
 78     private void close(Socket socket) {
 79         try {
 80             if (socket != null) {
 81                 socket.close();
 82             }
 83         } catch (IOException e) {
 84             e.printStackTrace();
 85         }
 86     }
 87 
 88     // 用於端口轉發
 89     private class Switch implements Runnable {
 90         private Socket host;
 91         private Socket remoteHost;
 92         private InputStream in;
 93         private OutputStream out;
 94 
 95         Switch(Socket host, Socket remoteHost, InputStream in, OutputStream out) {
 96             this.host = host;
 97             this.remoteHost = remoteHost;
 98             this.in = in;
 99             this.out = out;
100         }
101 
102         @Override
103         public void run() {
104             int length = 0;
105             byte[] buffer = new byte[1024];
106             try {
107                 while (!host.isClosed() && (length = in.read(buffer)) > -1) {
108                     clientList.put(host, new Date());
109                     out.write(buffer, 0, length);
110                 }
111             } catch (IOException e) {
112                 System.out.println("連接關閉");
113             } finally {
114                 close(host);
115                 close(remoteHost);
116             }
117         }
118     }
119 
120     // 用於清除失效連接和超時連接
121     private class AutoDestroy implements Runnable {
122 
123         @Override
124         public void run() {
125             Timer timer = new Timer();
126             timer.schedule(new TimerTask() {
127                 @Override
128                 public void run() {
129                     List<Socket> list = new LinkedList<>();
130                     System.out.println("開始掃描失效與超時連接");
131                     Date start = new Date();
132                     for (Socket socket : clientList.keySet()) {
133                         Date lastTime = clientList.get(socket);
134                         long time = new Date().getTime() - lastTime.getTime();
135                         if (socket.isClosed() || time / 1000 >= TIMEOUT) {
136                             list.add(socket);
137                         }
138                     }
139                     System.out.println("找到" + list.size() + "個,用時 : " + (new Date().getTime() - start.getTime()) + "毫秒");
140                     System.out.println("開始清除失效與超時連接");
141                     for (Socket socket : list) {
142                         try {
143                             clientList.remove(socket);
144                             socket.close();
145                         } catch (IOException e) {
146                             e.printStackTrace();
147                         }
148                     }
149                     System.out.println("當前連接數 : " + clientList.size());
150                 }
151             }, 30 * 1000, 30 * 1000);
152         }
153     }
154 
155     private class Terminal implements Runnable {
156         private ReaderUtil reader = new ReaderUtil();
157         private String format = "yyyy-MM-dd HH:mm:ss";
158         private SimpleDateFormat dateFormat = new SimpleDateFormat(format);
159 
160         @Override
161         public void run() {
162             while (!server.isClosed()) {
163                 System.out.print("請輸入命令 : ");
164                 String cmd = reader.readKB();
165                 handler(cmd);
166             }
167         }
168 
169         private void handler(String cmd) {
170             switch (cmd) {
171                 case "status":
172                     System.out.println("當前時間 : " + dateFormat.format(new Date()));
173                     System.out.println("總連接數 : " + clientList.size());
174                     for (Socket socket : clientList.keySet()) {
175                         long time = new Date().getTime() - clientList.get(socket).getTime();
176                         System.out.println("<" + socket.getRemoteSocketAddress().toString() + "> " + time / 1000);
177                     }
178                     break;
179             }
180         }
181     }
182 }

 ReaderUtil這個東西是我自己寫的從鍵盤讀一行的工具類 沒啥東西 你們需要自己寫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM