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()