2015-09-02 22 views
2

我想在C#中实现一个TCP转发器。具体地,应用程序:TCP异步套接字端口转发

  1. 监听到TCP端口并等待客户端,
  2. 当客户端连接,连接到远程主机,
  3. 等待输入数据上这两个连接和在两个端点之间交换数据(充当代理),
  4. 关闭一个连接当另一个由端点关闭时。

我已经适应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服务器)的进一步响应。

似乎有两个问题,我的实现,我不能调试:

  1. 在这种情况下,BeginReceiveStartReceive方法被调用,但没有数据从FTP服务器接收。我不认为这可能是FTP服务器问题(它是一个ProFTPD服务器),因为它是一个众所周知的FTP服务器。

  2. 每次连接建立和关闭时,线程数都会增加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"); 
     } 
    } 
} 

回答

1

的第一个问题是,代码将继续在目标插座连接失败(在接受循环)。在try/catch中使用continue;。当您调用第一个BeginReceive时,也无法保证套接字仍然处于启动状态。这些电话也需要打包。

总是将回调方法包装在try/catch中,否则应用程序可能会失败(在本例中为OnDataRecieve)。

修复并开始写出例外。他们肯定会给你一个关于错误的提示。

+0

良好的通话!我在'while(true)'中的异常情况下添加了'continue',并将所有方法都包含在try catch中。抛出的唯一例外是与之前的'EndReceive'相关的例外。线程数量和创建新连接仍然存在问题。 – Isaac

+0

重新启动FTP服务器并不能解决问题,所以这绝对是TCPForwarder中的一个错误。 – Isaac

+0

例外说什么? – jgauffin