Socket.Bind并连接使用本地地址 [英] Socket.Bind and Connect using local addresses

查看:129
本文介绍了Socket.Bind并连接使用本地地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要考验我的服务器/客户端应用程序,其中每个客户端都通过其IP地址已知的,我创建了多个网络适配器(见的如何创建.NET中的虚拟网络适配器?)。无论192.168.0.10和11现在相当于本地以太网适配器(10为真实的,11是环回适配器)。



客户端可以连接本身到服务器,只要它不绑定其套接字到特定地址。但是,如果这样做,服务器不知道的,在客户端(我想用发生超时绑定为安全起见,服务器自动检测该客户端连接本身看新连接的远程端点的IP地址:服务器会断开连接一次,如果它不知道IP地址 - 以前我使用多个虚拟机,但它使用了更多的RAM是不实际使用)。



下面是在我的服务器的代码,例如在192.168.0.10:1234



聆听

  IPEndPoint myEP =新IPEndPoint(myAddress,MyPort上); 
插槽listeningSocket =新的Socket(myAddress.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);
插槽acceptedSocket = listeningSocket.Accept();

下面是在我的客户端代码,如绑定到192.168.0.11(任何端口),并连接到192.168 .0.10:1234

  Socket套接字=新的Socket(svrAddress.AddressFamily,SocketType.Stream,ProtocolType.Tcp); 
socket.Bind(新IPEndPoint(myAddress,0)); //绑定到本地地址使用自动端口
socket.Connect(新IPEndPoint(svrAddress,svrPort)); //没有绑定做工精细,暂停时以绑定



我已经使用相应的IPv6地址试图同但我得到完全相同的结果。
。如果我绑定客户端上的相同地址(使用不同的端口比服务器),它工作正常。



任何想法,我做错了吗?



编辑这里是我的测试项目(它可能是有用的人)



服务器部分:

 使用系统;使用System.Net 
;使用的System.Net.Sockets
;使用System.Threading.Tasks
;

命名空间服务器
{
类节目
{
静态无效的主要(字串[] args)
{
ip地址[ ] IPS = Dns.GetHostEntry(Dns.GetHostName())AddressList中。

串线=的String.Empty;
,而(行!=Q)
{
//获取IP地址,侦听。
Console.WriteLine(IP上侦听:);
诠释计数= 0;
的foreach(ip地址IP在IPS)
Console.WriteLine({0}:{1},++算,ip.ToString());

串numString =到Console.ReadLine();
INT POS = Convert.ToInt32(numString) - 1;
ip地址myAddress = IPS [POS] //删除与否范围ID不会改变任何东西为localEndPoint下面将包含它没有物质b
$ B //绑定,并开始听什么$。
IPEndPoint myEP =新IPEndPoint(myAddress,12345);
插槽listeningSocket =新的Socket(myAddress.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);

IPEndPoint localEndPoint =(IPEndPoint)listeningSocket.LocalEndPoint;
Console.WriteLine(听上{0}:{1},localEndPoint.Address,localEndPoint.Port);

Task.Factory.StartNew(()=>
{

{
//接受新的连接,并发送一些虚拟的字节数组,然后关闭套接字
插槽acceptedSocket = listeningSocket.Accept();
IPEndPoint remoteEndPoint =(IPEndPoint)acceptedSocket.RemoteEndPoint;
Console.WriteLine(从{0}接受连接:{1 },remoteEndPoint.Address,remoteEndPoint.Port);
acceptedSocket.Send(新字节[] {0,1,2,3,4,5,6,7,8,9});
acceptedSocket.Close(5000);
Console.WriteLine( - =已完成= - 键入q退出,别的继续);
}
赶上(异常前)
{}
});

线=到Console.ReadLine();

//关闭监听套接字。
listeningSocket.Close();
}
}
}
}



客户端部分

 使用系统; 
使用System.Linq的;使用System.Net
;使用的System.Net.Sockets
;使用System.Threading.Tasks
;

命名空间客户端
{
类节目
{
静态无效的主要(字串[] args)
{
ip地址[ ] IPS = Dns.GetHostEntry(Dns.GetHostName())AddressList中。

串线=的String.Empty;
,而(行!=Q)
{
//获取IP地址连接到(如果它是一个IPv6删除范围ID)。
Console.WriteLine(IP连接到:);
诠释计数= 0;
的foreach(ip地址IP在IPS)
Console.WriteLine({0}:{1},++算,ip.ToString());

串numString =到Console.ReadLine();
INT POS = Convert.ToInt32(numString) - 1;
ip地址svrAddress = IPS [POS] .AddressFamily == AddressFamily.InterNetworkV6
?新的ip地址(IPS [POS] .GetAddressBytes())
:IPS [POS]

Console.WriteLine(连接到+ svrAddress);

//获取IP地址绑定上(可以选择无 - 还删除了作用域ID,如果它是一个IPv6)。
Console.WriteLine(IP绑定到:);
Console.WriteLine(0:无);
计数= 0;
ip地址[] = filteredIps ips.Where(I => i.AddressFamily == svrAddress.AddressFamily).ToArray();
的foreach(在filteredIps ip地址IP)
Console.WriteLine({0}:{1},++算,ip.ToString());

numString =到Console.ReadLine();
POS = Convert.ToInt32(numString) - 1;
IPEndPoint localEndPoint =(POS == -1)
?空
:新IPEndPoint(
filteredIps [POS] .AddressFamily == AddressFamily.InterNetworkV6
新ip地址(filteredIps [POS] .GetAddressBytes())
:filteredIps [POS]
,0);
Console.WriteLine(?绑定到+(localEndPoint == NULL无:localEndPoint.Address.ToString()));

//绑定到一个地址,如果我们选择。
Socket套接字=新的Socket(svrAddress.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
如果(localEndPoint!= NULL)
socket.Bind(localEndPoint);

Task.Factory.StartNew(()=>
{
尝试
{
//连接到服务器并接收该虚设字节数组,然后关闭套接字
socket.Connect(新IPEndPoint(svrAddress,12345));
IPEndPoint remoteEndPoint =(IPEndPoint)socket.RemoteEndPoint;
Console.WriteLine(连接到{0 }:{1},remoteEndPoint.Address,remoteEndPoint.Port);
字节[]缓冲液=新的字节[10];
Console.WriteLine((socket.Receive(缓冲器)==缓冲器。 ?长)收到消息:错误的信息);
socket.Close();
}
赶上(异常前)
{
//一个出现异常:应该是到一个SocketException由于超时,如果我们选择了绑定到一个地址。
Console.WriteLine(错误:+ ex.ToString());
}
Console.WriteLine( - =已完成= - 键入q退出,别的继续);
});

线=到Console.ReadLine();
}
}
}
}


解决方案

其实这是一个配置问题,我的网络适配器和它与做弱和强主机模式



这是我读过(使用特定的网络接口,在Windows套接字)在Windows以前绑定到Vista将只用于传入流量的工作,它不会做传出通信东西。



与Vista这是可能的,但默认情况下它不会工作开始:你需要让弱主机模式使用

  netsh接口的IPv4设置界面中的环回weakhostreceive =启用
netsh接口的IPv4设置界面中的环回weakhostsend =启用

请参阅的http://博客。 loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/ 获取更多信息。



修改



其实,而不是创建几个回环适配器,并改变他们的主机模式,这是一个很大更好,更容易在不同的网络比你实际对刚刚创建一个回环网卡,给它几个IP地址IP,然后只使用这些IP地址为您的测试。这样,有没有路由的问题,你肯定一切都保持地方(因为是真正的和回送适配器之间没有路由)。


To test my server/client application, where each client is known by its IP address, I created several network adapters (see How to Create a Virtual Network Adapter in .NET?). Both 192.168.0.10 and 11 now correspond to local ethernet adaptors (10 being the "real" one, 11 being a loopback adapter).

The client can Connect itself to the server as long as it doesn't Bind its socket to a specific address. But if it does, the server doesn't notice anything and a timeout occurs in the client (I want to use Bind as for security reasons the server automatically detects which client is connecting itself by looking at the IP address of the remote end point of the new connection: the server will drop the connection at once if it doesn't know the IP address - previously I was using several virtual machines, but it uses a lot more RAM and is less practical to use).

Here's the code in my server, listening eg on 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();

Here's the code in my client, binding eg to 192.168.0.11 (any port) and connecting to 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

I've tried the same using the corresponding IPv6 addresses but I get the exact same result. If I Bind the client on the same address (using a different port than the server), it works fine.

Any idea what I'm doing wrong?

EDIT Here is my test projects (it might be useful to someone)

Server part:

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();
            }
        }
    }
}

Client part

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();
            }
        }
    }
}

解决方案

Actually it was a configuration issue with my network adapters and it has to do with "Weak and Strong Host model".

From what I've read ( Using a specific network interface for a socket in windows ) binding on Windows previous to Vista would only work for incoming traffic, and it wouldn't do anything for outgoing traffic.

Starting with Vista it's possible but by default it won't work: you need to allow the "weak host model" using

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

See http://blog.loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/ for more info.

EDIT

Actually, instead of creating several loopback adapters and changing their host model, it's a lot better and easier to just create one loopback adapter, give it several IP addresses on a different network than your real IP, and then only use those IPs for your test. That way there's no routing issue, and you're sure everything stays local (as there's no routing between the real and loopback adapter).

这篇关于Socket.Bind并连接使用本地地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆