我想在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