zoukankan      html  css  js  c++  java
  • 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这个东西是我自己写的从键盘读一行的工具类 没啥东西 你们需要自己写

    来自1942年冬季攻势中的中央集团军的037号17吨救援拖车
  • 相关阅读:
    java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener问题解决
    开发人员系统功能设计常用办公软件分享
    微信自定义菜单url默认80端口问题解决
    Servlet再度学习
    JSP九大内置对象
    linux下安装apache(httpd-2.4.3版本)各种坑
    Ajax原理学习
    Shell脚本了解
    生成Webservice的两种方式(Axis2,CXf2.x)
    Webservice发布
  • 原文地址:https://www.cnblogs.com/panther1942/p/11109575.html
Copyright © 2011-2022 走看看