端口转发

端口转发工具

  • 详细代理
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>.
相关推荐
运维@小兵2 小时前
Spring-AI系列——Tool Calling获取当前时间
java·后端·spring
Java水解2 小时前
前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践
前端·后端
镜花水月linyi3 小时前
Java 线程创建的完整链路:从 Java 层 → JVM 层 → 操作系统层
java·后端·面试
文心快码BaiduComate3 小时前
我用文心快码Spec 模式搓了个“pre作弊器”,妈妈再也不用担心我开会忘词了(附源码)
前端·后端·程序员
aiopencode3 小时前
iOS 性能监控 运行时指标与系统行为的多工具协同方案
后端
E***U9453 小时前
从新手到入门:如何判断自己是否真的学会了 Spring Boot
数据库·spring boot·后端
招风的黑耳4 小时前
智慧养老项目:当SpringBoot遇到硬件,如何优雅地处理异常与状态管理?
java·spring boot·后端
回家路上绕了弯4 小时前
分布式锁原理深度解析:从理论到实践
分布式·后端
磊磊磊磊磊4 小时前
用AI做了个排版工具,分享一下如何高效省钱地用AI!
前端·后端·react.js