2012-07-02 63 views
3

爲了測試我的服務器/客戶端應用程序,其中每個客戶端的IP地址都已知,我創建了多個網絡適配器(請參閱How to Create a Virtual Network Adapter in .NET?)。現在192.168.0.10和11都對應於本地以太網適配器(10是「真實」的,11是回送適配器)。使用本地地址的Socket.Bind和Connect

客戶端可以自己到服務器Connect只要它沒有Bind它的套接字到一個特定的地址。但如果是這樣,服務器不會注意到任何事情,並且客戶端發生超時(我想使用Bind作爲安全原因,服務器通過查看遠程端點的IP地址自動檢測哪個客戶端正在連接它自己新的連接:如果服務器不知道IP地址,它將立即斷開連接 - 以前我使用的是多個虛擬機,但它使用更多的RAM並且使用起來不太實際)。

下面是我的服務器代碼,聽例如在192.168.0.10:1234

IPEndPoint myEP = new IPEndPoint(myAddress, myPort); 
Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
listeningSocket.Bind(myEP); 
listeningSocket.Listen(50); 
Socket acceptedSocket = listeningSocket.Accept(); 

下面是我的客戶端的代碼,結合如到192.168.0.11(任何端口),並連接到192.168.0.10: 1234

Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
socket.Bind(new IPEndPoint(myAddress, 0)); // Bind to local address using automatic port 
socket.Connect(new IPEndPoint(svrAddress, svrPort)); // Works fine without Bind, timeout with Bind 

我試過同樣的使用相應的IPv6地址,但我得到了完全相同的結果。 如果我Bind客戶端在相同的地址(使用與服務器不同的端口),它工作正常。

任何想法我做錯了什麼?

編輯這裏是我的測試項目(它可能是有用的人)

服務器部分:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Threading.Tasks; 

namespace Server 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList; 

      string line = string.Empty; 
      while (line != "q") 
      { 
       // Gets the IP address to listen on. 
       Console.WriteLine("IP to listen on:"); 
       int count = 0; 
       foreach (IPAddress ip in ips) 
        Console.WriteLine("{0}: {1}", ++count, ip.ToString()); 

       string numString = Console.ReadLine(); 
       int pos = Convert.ToInt32(numString) - 1; 
       IPAddress myAddress = ips[pos]; // Removing or not the scope ID doesn't change anything as "localEndPoint" below will contain it no matter what 

       // Binds and starts listening. 
       IPEndPoint myEP = new IPEndPoint(myAddress, 12345); 
       Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
       listeningSocket.Bind(myEP); 
       listeningSocket.Listen(50); 

       IPEndPoint localEndPoint = (IPEndPoint)listeningSocket.LocalEndPoint; 
       Console.WriteLine("Listening on {0}:{1}", localEndPoint.Address, localEndPoint.Port); 

       Task.Factory.StartNew(() => 
        { 
         try 
         { 
          // Accepts new connections and sends some dummy byte array, then closes the socket. 
          Socket acceptedSocket = listeningSocket.Accept(); 
          IPEndPoint remoteEndPoint = (IPEndPoint)acceptedSocket.RemoteEndPoint; 
          Console.WriteLine("Accepted connection from {0}:{1}.", remoteEndPoint.Address, remoteEndPoint.Port); 
          acceptedSocket.Send(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); 
          acceptedSocket.Close(5000); 
          Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue"); 
         } 
         catch (Exception ex) 
         { } 
        }); 

       line = Console.ReadLine(); 

       // Closes the listening socket. 
       listeningSocket.Close(); 
      } 
     } 
    } 
} 

客戶端部分

using System; 
using System.Linq; 
using System.Net; 
using System.Net.Sockets; 
using System.Threading.Tasks; 

namespace Client 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList; 

      string line = string.Empty; 
      while (line != "q") 
      { 
       // Gets the IP address to connect to (removes the "scope ID" if it's an IPv6). 
       Console.WriteLine("IP to connect to:"); 
       int count = 0; 
       foreach (IPAddress ip in ips) 
        Console.WriteLine("{0}: {1}", ++count, ip.ToString()); 

       string numString = Console.ReadLine(); 
       int pos = Convert.ToInt32(numString) - 1; 
       IPAddress svrAddress = ips[pos].AddressFamily == AddressFamily.InterNetworkV6 
        ? new IPAddress(ips[pos].GetAddressBytes()) 
        : ips[pos]; 

       Console.WriteLine("Connecting to " + svrAddress); 

       // Gets the IP address to bind on (can chose "none" - also removes the "scope ID" if it's an IPv6). 
       Console.WriteLine("IP to bind to:"); 
       Console.WriteLine("0: none"); 
       count = 0; 
       IPAddress[] filteredIps = ips.Where(i => i.AddressFamily == svrAddress.AddressFamily).ToArray(); 
       foreach (IPAddress ip in filteredIps) 
        Console.WriteLine("{0}: {1}", ++count, ip.ToString()); 

       numString = Console.ReadLine(); 
       pos = Convert.ToInt32(numString) - 1; 
       IPEndPoint localEndPoint = (pos == -1) 
        ? null 
        : new IPEndPoint(
         filteredIps[pos].AddressFamily == AddressFamily.InterNetworkV6 
          ? new IPAddress(filteredIps[pos].GetAddressBytes()) 
          : filteredIps[pos] 
         , 0); 
       Console.WriteLine("Binding to " + (localEndPoint == null ? "none" : localEndPoint.Address.ToString())); 

       // Binds to an address if we chose to. 
       Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
       if (localEndPoint != null) 
        socket.Bind(localEndPoint); 

       Task.Factory.StartNew(() => 
       { 
        try 
        { 
         // Connects to the server and receives the dummy byte array, then closes the socket. 
         socket.Connect(new IPEndPoint(svrAddress, 12345)); 
         IPEndPoint remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; 
         Console.WriteLine("Connected to {0}:{1}", remoteEndPoint.Address, remoteEndPoint.Port); 
         byte[] buffer = new byte[10]; 
         Console.WriteLine((socket.Receive(buffer) == buffer.Length) ? "Received message" : "Incorrect message"); 
         socket.Close(); 
        } 
        catch (Exception ex) 
        { 
         // An exception occured: should be a SocketException due to a timeout if we chose to bind to an address. 
         Console.WriteLine("ERROR: " + ex.ToString()); 
        } 
        Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue"); 
       }); 

       line = Console.ReadLine(); 
      } 
     } 
    } 
} 
+0

嘗試呈現展現您問題的最小可能示例代碼。這裏的大多數用戶不想調試你的長代碼。 – japreiss

+0

@japreiss我首先添加了一個小樣本(服務器5行,客戶端3),然後我又添加了兩個簡單測試項目的完整源代碼(我認爲它可以幫助某人,因爲它工作正常,但也許它也嚇倒了想要回答的人)。 – user276648

回答

7

其實這是一個配置問題,我的網絡適配器,它與「弱和強主機型號」有關。

從我讀過的(Using a specific network interface for a socket in windows)Windows Vista之前的Windows綁定只適用於傳入流量,它不會爲傳出流量做任何事情。

與Vista開始是可能的,但默認情況下它不會工作:你需要允許使用

netsh interface ipv4 set interface "loopback" weakhostreceive=enabled 
netsh interface ipv4 set interface "loopback" weakhostsend=enabled 

更多信息,請參見http://blog.loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/「弱主機模式」。

編輯

事實上,而不是創建幾個迴環適配器和改變他們的主機模型,這是一個很大更好,更容易只需創建一個迴環網卡,給它幾個IP地址在不同的網絡上不是你的真實IP,然後只使用這些IP進行測試。這樣就沒有路由問題,並且你確定所有東西都保持在本地(因爲真實和回送適配器之間沒有路由)。

5

在服務器中使用以下代碼來綁定同一端口上所有接口上的連接。

// Binds and starts listening. 
IPEndPoint myEP = new IPEndPoint(IPAddress.Any, 12345);