端口转发

端口转发工具

  • 详细代理
python 复制代码
#!/usr/local/python3/bin/python3
# -*- coding:utf-8 -*-
"""
高性能端口转发工具 - 优化版
简洁、高效、稳定的端口转发解决方案
"""

import socket
import threading
import argparse
import sys
import logging
import select
from typing import Optional, Tuple, List
import time
import signal
import os

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)

class ConnectionStats:
    """连接统计"""
    def __init__(self):
        self.total_connections = 0
        self.active_connections = 0
        self.bytes_transferred = 0
        self.start_time = time.time()
        self.lock = threading.Lock()
    
    def connection_started(self):
        with self.lock:
            self.total_connections += 1
            self.active_connections += 1
    
    def connection_ended(self, bytes_count: int = 0):
        with self.lock:
            self.active_connections -= 1
            self.bytes_transferred += bytes_count
    
    def get_stats(self) -> dict:
        with self.lock:
            uptime = time.time() - self.start_time
            return {
                'uptime': uptime,
                'total_connections': self.total_connections,
                'active_connections': self.active_connections,
                'bytes_transferred': self.bytes_transferred,
                'connections_per_second': self.total_connections / uptime if uptime > 0 else 0,
                'bytes_per_second': self.bytes_transferred / uptime if uptime > 0 else 0
            }

class PortForwarder:
    def __init__(
        self,
        local_port: int,
        remote_host: str,
        remote_port: int,
        bind_host: str = '0.0.0.0',
        buffer_size: int = 8192,
        backlog: int = 128,
        timeout: int = 30,
        max_connections: int = 1000
    ):
        """
        初始化端口转发器
        
        Args:
            local_port: 本地监听端口
            remote_host: 目标主机
            remote_port: 目标端口
            bind_host: 绑定地址
            buffer_size: 缓冲区大小
            backlog: 连接队列大小
            timeout: 连接超时时间(秒)
            max_connections: 最大并发连接数
        """
        self.local_port = local_port
        self.remote_host = remote_host
        self.remote_port = remote_port
        self.bind_host = bind_host
        self.buffer_size = buffer_size
        self.backlog = backlog
        self.timeout = timeout
        self.max_connections = max_connections
        
        self.running = False
        self.server_socket = None
        self.stats = ConnectionStats()
        
        # 设置信号处理
        signal.signal(signal.SIGINT, self._signal_handler)
        signal.signal(signal.SIGTERM, self._signal_handler)
    
    def _signal_handler(self, signum, frame):
        """信号处理函数"""
        logger.info(f"收到信号 {signum},正在停止服务...")
        self.stop()
    
    def _create_remote_connection(self) -> Optional[socket.socket]:
        """创建到远程服务器的连接"""
        try:
            remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            remote_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
            remote_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            remote_socket.settimeout(self.timeout)
            
            remote_socket.connect((self.remote_host, self.remote_port))
            remote_socket.setblocking(False)
            
            return remote_socket
            
        except Exception as e:
            logger.error(f"创建远程连接失败: {e}")
            return None
    
    def _forward_connection(self, client_socket: socket.socket, client_addr: Tuple[str, int]):
        """处理单个连接的数据转发"""
        remote_socket = None
        bytes_transferred = 0
        
        try:
            self.stats.connection_started()
            logger.info(f"新连接: {client_addr[0]}:{client_addr[1]}")
            
            # 创建远程连接
            remote_socket = self._create_remote_connection()
            if not remote_socket:
                return
            
            # 设置客户端socket为非阻塞
            client_socket.setblocking(False)
            
            # 使用select进行高效的数据转发
            sockets = [client_socket, remote_socket]
            
            while self.running:
                try:
                    # 使用select监听socket状态
                    readable, writable, exceptional = select.select(sockets, [], sockets, 1.0)
                    
                    # 处理异常socket
                    for sock in exceptional:
                        logger.debug(f"Socket异常: {sock}")
                        return
                    
                    # 转发可读数据
                    for sock in readable:
                        if sock is client_socket:
                            source, dest = client_socket, remote_socket
                            direction = "client -> server"
                        else:
                            source, dest = remote_socket, client_socket
                            direction = "server -> client"
                        
                        try:
                            data = source.recv(self.buffer_size)
                            if not data:
                                logger.debug(f"连接正常关闭: {direction}")
                                return
                            
                            # 确保所有数据都被发送
                            total_sent = 0
                            while total_sent < len(data):
                                sent = dest.send(data[total_sent:])
                                if sent == 0:
                                    raise RuntimeError("Socket连接断开")
                                total_sent += sent
                            
                            bytes_transferred += len(data)
                            logger.debug(f"{direction}: {len(data)}字节")
                            
                        except (socket.error, BlockingIOError) as e:
                            logger.debug(f"Socket错误 {direction}: {e}")
                            return
                            
                except (socket.error, OSError) as e:
                    if self.running:
                        logger.debug(f"Select错误: {e}")
                    break
                except Exception as e:
                    logger.error(f"转发错误: {e}")
                    break
                    
        except Exception as e:
            logger.error(f"处理连接时出错: {e}")
        finally:
            # 关闭连接
            try:
                client_socket.close()
            except:
                pass
            
            if remote_socket:
                try:
                    remote_socket.close()
                except:
                    pass
            
            self.stats.connection_ended(bytes_transferred)
            logger.info(f"连接关闭: {client_addr[0]}:{client_addr[1]}, 传输 {bytes_transferred} 字节")
    
    def _monitor_stats(self):
        """监控统计信息"""
        while self.running:
            time.sleep(10)  # 每10秒输出一次统计信息
            stats = self.stats.get_stats()
            logger.info(
                f"统计: 运行 {int(stats['uptime'])}秒, "
                f"总连接 {stats['total_connections']}, "
                f"活动连接 {stats['active_connections']}, "
                f"总流量 {self._format_bytes(stats['bytes_transferred'])}"
            )
    
    def _format_bytes(self, bytes_count: int) -> str:
        """格式化字节数为可读格式"""
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if bytes_count < 1024.0:
                return f"{bytes_count:.2f}{unit}"
            bytes_count /= 1024.0
        return f"{bytes_count:.2f}PB"
    
    def start(self):
        """启动端口转发服务"""
        try:
            # 创建服务器socket
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
            
            # 设置socket选项以提高性能
            self.server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            
            # 对于高并发场景,调整接收缓冲区
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
            
            # 绑定到本地端口
            self.server_socket.bind((self.bind_host, self.local_port))
            self.server_socket.listen(self.backlog)
            self.server_socket.settimeout(1.0)  # 设置超时以便可以检查运行状态
            
            logger.info(f"端口转发服务启动 (PID: {os.getpid()})")
            logger.info(f"监听: {self.bind_host}:{self.local_port}")
            logger.info(f"转发到: {self.remote_host}:{self.remote_port}")
            logger.info(f"最大连接数: {self.max_connections}")
            logger.info(f"缓冲区大小: {self.buffer_size}字节")
            
            self.running = True
            
            # 启动统计监控线程
            stats_thread = threading.Thread(target=self._monitor_stats, daemon=True)
            stats_thread.start()
            
            # 主连接处理循环
            while self.running:
                try:
                    # 接受客户端连接
                    client_socket, client_addr = self.server_socket.accept()
                    
                    # 检查当前连接数
                    current_stats = self.stats.get_stats()
                    if current_stats['active_connections'] >= self.max_connections:
                        logger.warning(f"达到最大连接数限制,拒绝连接: {client_addr}")
                        client_socket.close()
                        continue
                    
                    # 为新连接创建处理线程
                    client_thread = threading.Thread(
                        target=self._forward_connection,
                        args=(client_socket, client_addr),
                        daemon=True
                    )
                    client_thread.start()
                    
                except socket.timeout:
                    # 超时是正常的,继续循环
                    continue
                except KeyboardInterrupt:
                    logger.info("收到中断信号")
                    break
                except Exception as e:
                    if self.running:
                        logger.error(f"接受连接时出错: {e}")
                        
        except Exception as e:
            logger.error(f"启动服务失败: {e}")
        finally:
            self.stop()
    
    def stop(self):
        """停止端口转发服务"""
        if not self.running:
            return
            
        self.running = False
        
        if self.server_socket:
            try:
                self.server_socket.close()
            except:
                pass
        
        # 输出最终统计
        stats = self.stats.get_stats()
        logger.info("服务已停止")
        logger.info(f"最终统计:")
        logger.info(f"  运行时间: {int(stats['uptime'])}秒")
        logger.info(f"  总连接数: {stats['total_connections']}")
        logger.info(f"  总流量: {self._format_bytes(stats['bytes_transferred'])}")
        logger.info(f"  平均连接/秒: {stats['connections_per_second']:.2f}")
        logger.info(f"  平均流量/秒: {self._format_bytes(stats['bytes_per_second'])}/秒")

def main():
    """主函数:解析参数并启动服务"""
    parser = argparse.ArgumentParser(
        description='高性能端口转发工具',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
使用示例:
  # 基本转发
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80
  
  # 绑定到特定IP
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -b 127.0.0.1
  
  # 高并发配置
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -c 1000 -bs 16384
  
  # 启用详细日志
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -v
  
性能调优建议:
  1. 对于大量小连接: 减小buffer_size,增加max_connections
  2. 对于大流量传输: 增大buffer_size,调整backlog
  3. 对于高延迟网络: 适当增加timeout值
  
信号支持:
  Ctrl+C 或 kill PID: 优雅停止服务
        '''
    )
    
    parser.add_argument('-l', '--local-port', type=int, required=True,
                       help='本地监听端口 (1-65535)')
    parser.add_argument('-r', '--remote-host', required=True,
                       help='目标主机地址')
    parser.add_argument('-p', '--remote-port', type=int, required=True,
                       help='目标端口 (1-65535)')
    parser.add_argument('-b', '--bind-host', default='0.0.0.0',
                       help='绑定地址 (默认: 0.0.0.0)')
    parser.add_argument('-v', '--verbose', action='store_true',
                       help='启用详细日志')
    parser.add_argument('-bs', '--buffer-size', type=int, default=8192,
                       help='缓冲区大小 (默认: 8192)')
    parser.add_argument('-bl', '--backlog', type=int, default=128,
                       help='连接队列大小 (默认: 128)')
    parser.add_argument('-t', '--timeout', type=int, default=30,
                       help='连接超时时间(秒) (默认: 30)')
    parser.add_argument('-c', '--max-connections', type=int, default=1000,
                       help='最大并发连接数 (默认: 1000)')
    
    args = parser.parse_args()
    
    # 设置日志级别
    if args.verbose:
        logger.setLevel(logging.DEBUG)
        # 控制台输出详细日志
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
        console_handler.setFormatter(console_formatter)
        logger.addHandler(console_handler)
    else:
        # 生产环境日志格式
        logger.setLevel(logging.INFO)
    
    # 验证参数
    if not (1 <= args.local_port <= 65535):
        logger.error("本地端口必须在1-65535之间")
        sys.exit(1)
    
    if not (1 <= args.remote_port <= 65535):
        logger.error("远程端口必须在1-65535之间")
        sys.exit(1)
    
    # 检查目标主机是否可达
    try:
        socket.gethostbyname(args.remote_host)
    except socket.error:
        logger.warning(f"无法解析目标主机: {args.remote_host}")
    
    # 创建并启动转发器
    forwarder = PortForwarder(
        local_port=args.local_port,
        remote_host=args.remote_host,
        remote_port=args.remote_port,
        bind_host=args.bind_host,
        buffer_size=args.buffer_size,
        backlog=args.backlog,
        timeout=args.timeout,
        max_connections=args.max_connections
    )
    
    try:
        forwarder.start()
    except KeyboardInterrupt:
        logger.info("程序被用户中断")
    except Exception as e:
        logger.error(f"程序运行出错: {e}")
        sys.exit(1)

if __name__ == '__main__':
    main()
  • 使用说明,比如转发到百度
yaml 复制代码
使用示例:
  # 基本转发
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80
  
  # 绑定到特定IP
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -b 127.0.0.1
  
  # 高并发配置
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -c 1000 -bs 16384
  
  # 启用详细日志
  python port_forwarder.py -l 8080 -r 192.168.1.100 -p 80 -v

[root@test ~]# ./port_forwarde -l  8080   -r 111.63.65.103 -p 80 
2025-12-15 15:01:23 [INFO] 端口转发服务启动 (PID: 106578)
2025-12-15 15:01:23 [INFO] 监听: 0.0.0.0:8080
2025-12-15 15:01:23 [INFO] 转发到: 111.63.65.103:80
2025-12-15 15:01:23 [INFO] 最大连接数: 1000
2025-12-15 15:01:23 [INFO] 缓冲区大小: 8192字节
2025-12-15 15:01:33 [INFO] 统计: 运行 10秒, 总连接 0, 活动连接 0, 总流量 0.00B
2025-12-15 15:01:38 [INFO] 新连接: 192.168.127.1:59964
2025-12-15 15:01:38 [INFO] 连接关闭: 192.168.127.1:59964, 传输 301 字节
2025-12-15 15:01:43 [INFO] 统计: 运行 20秒, 总连接 1, 活动连接 0, 总流量 301.00B
2025-12-15 15:01:51 [INFO] 新连接: 192.168.127.1:59966
2025-12-15 15:01:51 [INFO] 连接关闭: 192.168.127.1:59966, 传输 301 字节
2025-12-15 15:01:52 [INFO] 新连接: 192.168.127.1:59968
2025-12-15 15:01:52 [INFO] 连接关闭: 192.168.127.1:59968, 传输 301 字节
2025-12-15 15:01:53 [INFO] 新连接: 192.168.127.1:59970
2025-12-15 15:01:53 [INFO] 连接关闭: 192.168.127.1:59970, 传输 301 字节
2025-12-15 15:01:53 [INFO] 统计: 运行 30秒, 总连接 4, 活动连接 0, 总流量 1.18KB
2025-12-15 15:02:03 [INFO] 统计: 运行 40秒, 总连接 4, 活动连接 0, 总流量 1.18KB
^C2025-12-15 15:02:12 [INFO] 收到信号 2,正在停止服务...
2025-12-15 15:02:12 [INFO] 服务已停止
2025-12-15 15:02:12 [INFO] 最终统计:
2025-12-15 15:02:12 [INFO]   运行时间: 48秒
2025-12-15 15:02:12 [INFO]   总连接数: 4
2025-12-15 15:02:12 [INFO]   总流量: 1.18KB
2025-12-15 15:02:12 [INFO]   平均连接/秒: 0.08
2025-12-15 15:02:12 [INFO]   平均流量/秒: 24.78B/秒
  • 访问端
ini 复制代码
[ccodrunner@aly_fs_20 tmp]$ curl  192.168.127.2:8080
<a href="http://www.baidu.com/">Moved Permanently</a>.
相关推荐
神奇小汤圆18 分钟前
浅析二叉树、B树、B+树和MySQL索引底层原理
后端
文艺理科生27 分钟前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling28 分钟前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
南极企鹅30 分钟前
springBoot项目有几个端口
java·spring boot·后端
Luke君6079731 分钟前
Spring Flux方法总结
后端
define952735 分钟前
高版本 MySQL 驱动的 DNS 陷阱
后端
忧郁的Mr.Li1 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
暮色妖娆丶2 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
Coder_Boy_2 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring
Java后端的Ai之路2 小时前
【Spring全家桶】-一文弄懂Spring Cloud Gateway
java·后端·spring cloud·gateway