2015-09-02 135 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