在windows下, 利用netsh可以实现端口转发功能, 能满足大部分日常需求, 但在某些情况下, 在后端节点不可用的时候, 需要将数据转发至另一节点, 这时就只能自行编码实现了。
本文将使用C#做一个简单的实现: 完整代码
解决思路
- 因为需要转发数据, 所以程序肯定是要监听端口的
- 为了避免数据混乱和复杂性, 这里每个端口只接收一个连接, 所以接收到新连接后, 关闭端口监听
- 若长时间没有数据通讯, 则主动关闭连接, 释放资源
- 开启两个工作线程, 一个叫代理线程, 一个叫转发线程
代理线程工作流程
代理线程负责接收外来连接, 并确保同一时间只有一个外来连接, 接收到连接后读取数据, 读取到数据后, 将数据转发到指定目标地址。
graph LR
listen[监听]
accept[接受连接]
read[读取数据]
forwarding[转发数据]
listen-->accept
accept-->read
read-->forwarding
forwarding-->read
主要代码:server = new TcpListener(listen);
server.Start(1);
client = server.AcceptTcpClient();
server.Stop(); //停止监听, 同一时间只接收一个连接
server = null;
listenStream = client.GetStream();
listenStream.ReadTimeout = READ_TIMEOUT;
while (true)
{
int len = listenStream.Read(buffer, 0, BUFF_SIZE);
if (len > 0)
{
byte[] tmp = new byte[len];
Array.Copy(buffer, tmp, len);
Forwarding(tmp); //转发数据
}
else
{
//.. 处理连接中断
}
}
转发线程工作流程
转发线程负责连接目标地址, 一个连不上再去连接下一个, 当连上后, 等待接收数据, 接收到数据后, 将数据转发到代理连接上去。
主要代码:client = new TcpClient();
var result = client.BeginConnect(currentEndPoint.Address, currentEndPoint.Port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(CONNECT_TIMEOUT));
if (!success) //超时处理
{
Console.WriteLine("connect {0} timeout", currentEndPoint);
continue;
}
client.EndConnect(result);
forwardingStream = client.GetStream();
forwardingStream.ReadTimeout = READ_TIMEOUT;
while (true)
{
int len = forwardingStream.Read(buffer, 0, BUFF_SIZE);
if (len > 0)
{
byte[] tmp = new byte[len];
Array.Copy(buffer, tmp, len);
Response(tmp);
}
else
{
//.. 处理连接中断
}
}