我想在C#中实现一个TCP转发器。具体地,应用程序:TCP异步套接字端口转发
- 监听到TCP端口并等待客户端,
- 当客户端连接,连接到远程主机,
- 等待输入数据上这两个连接和在两个端点之间交换数据(充当代理),
- 关闭一个连接当另一个由端点关闭时。
我已经适应Simple TCP Forwader(由加西亚)转发的端口范围,使得
TCPForwarder.exe 10.1.1.1 192.168.1.100 1000 1100 2000
将转发端口1000-1100到远程主机192.168.1.100端口上接收到的10.1.1.1任何分组2000-2100。我已经使用这个来暴露NAT后面的FTP服务器。
通过运行上述命令中,客户端能够连接到FTP服务器,并在输出到预计控制台以下模式(参考代码):
0 StartReceive: BeginReceive
1 StartReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: Close (0 read)
0 OnDataReceive: EndReceive
0 OnDataReceive: Close (exception)
但后成功地连接多次(在Filezilla中按F5),不会收到来自TCPForwarder(和FTP服务器)的进一步响应。
似乎有两个问题,我的实现,我不能调试:
在这种情况下,BeginReceive在StartReceive方法被调用,但没有数据从FTP服务器接收。我不认为这可能是FTP服务器问题(它是一个ProFTPD服务器),因为它是一个众所周知的FTP服务器。
每次连接建立和关闭时,线程数都会增加1.我认为垃圾回收并不能解决这个问题。线程数量持续增加,强制garabage收集器运行也不会减少。我认为我的代码中有一些泄漏也导致问题#1。
编辑:
重新启动FTP服务器并没有解决这个问题,所以肯定是有在TCPForwarder的错误。
@jgauffin指出的一些问题已在下面的代码中修复。
下面是完整的代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Threading;
namespace TCPForwarder
{
class Program
{
private class State
{
public int ID { get; private set; } // for debugging purposes
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(int id, Socket source, Socket destination)
{
ID = id;
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
public class TcpForwarder
{
public void Start(IPEndPoint local, IPEndPoint remote)
{
Socket MainSocket;
try
{
MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(local);
MainSocket.Listen(10);
}
catch (Exception exp)
{
Console.WriteLine("Error on listening to " + local.Port + ": " + exp.Message);
return;
}
while (true)
{
// Accept a new client
var socketSrc = MainSocket.Accept();
var socketDest = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
// Connect to the endpoint
socketDest.Connect(remote);
}
catch
{
socketSrc.Shutdown(SocketShutdown.Both);
socketSrc.Close();
Console.WriteLine("Exception in connecting to remote host");
continue;
}
// Wait for data sent from client and forward it to the endpoint
StartReceive(0, socketSrc, socketDest);
// Also, wait for data sent from endpoint and forward it to the client
StartReceive(1, socketDest, socketSrc);
}
}
private static void StartReceive(int id, Socket src, Socket dest)
{
var state = new State(id, src, dest);
Console.WriteLine("{0} StartReceive: BeginReceive", id);
try
{
src.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
catch
{
Console.WriteLine("{0} Exception in StartReceive: BeginReceive", id);
}
}
private static void OnDataReceive(IAsyncResult result)
{
State state = null;
try
{
state = (State)result.AsyncState;
Console.WriteLine("{0} OnDataReceive: EndReceive", state.ID);
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
Console.WriteLine("{0} OnDataReceive: BeginReceive", state.ID);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
else
{
Console.WriteLine("{0} OnDataReceive: Close (0 read)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
catch
{
if (state!=null)
{
Console.WriteLine("{0} OnDataReceive: Close (exception)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
}
}
static void Main(string[] args)
{
List<Socket> sockets = new List<Socket>();
int srcPortStart = int.Parse(args[2]);
int srcPortEnd = int.Parse(args[3]);
int destPortStart = int.Parse(args[4]);
List<Thread> threads = new List<Thread>();
for (int i = 0; i < srcPortEnd - srcPortStart + 1; i++)
{
int srcPort = srcPortStart + i;
int destPort = destPortStart + i;
TcpForwarder tcpForwarder = new TcpForwarder();
Thread t = new Thread(new ThreadStart(() => tcpForwarder.Start(
new IPEndPoint(IPAddress.Parse(args[0]), srcPort),
new IPEndPoint(IPAddress.Parse(args[1]), destPort))));
t.Start();
threads.Add(t);
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("All threads are closed");
}
}
}
良好的通话!我在'while(true)'中的异常情况下添加了'continue',并将所有方法都包含在try catch中。抛出的唯一例外是与之前的'EndReceive'相关的例外。线程数量和创建新连接仍然存在问题。 – Isaac
重新启动FTP服务器并不能解决问题,所以这绝对是TCPForwarder中的一个错误。 – Isaac
例外说什么? – jgauffin