Socket.Bind和Connect使用本地地址

为了测试我的服务器/客户端应用程序,其中每个客户端的IP地址是已知的,我创建了几个网络适配器(请参阅如何在.NET中创建虚拟网络适配器? )。 192.168.0.10和11现在都对应于本地以太网适配器(10是“真正的”,11是环回适配器)。

客户端可以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(); } } } } 

实际上这是我的网络适配器的配置问题,它与“ 弱和强主机模型 ”有关。

从我所读到的( 在Windows中使用特定的网络接口,用于 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进行测试,这样更好更容易。 这样就没有路由问题了,你确定一切都保持在本地(因为实际和环回适配器之间没有路由)。

使用服务器中的以下代码绑定同一端口上所有接口上的连接。

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