C# 使用本地地址的 Socket.Bind 和 Connect

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11290323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 17:15:49  来源:igfitidea点击:

Socket.Bind and Connect using local addresses

c#sockets

提问by user276648

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).

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

The client can Connectitself to the server as long as it doesn't Bindits 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 Bindas 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).

客户端可以Connect自己连接到服务器,只要Bind它的套接字不连接到特定地址。但是如果是这样,服务器不会注意到任何事情,并且客户端会发生超时(Bind出于安全原因,我想使用服务器通过查看远程端点的 IP 地址来自动检测哪个客户端正在连接自己)新连接:如果服务器不知道 IP 地址,它将立即断开连接 - 以前我使用了多个虚拟机,但它使用了更多的 RAM 并且不太实用)。

Here's the code in my server, listening eg on 192.168.0.10:1234

这是我服务器中的代码,例如在 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

这是我的客户端中的代码,例如绑定到 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

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

我已经尝试使用相应的 IPv6 地址进行相同的操作,但得到了完全相同的结果。如果我Bind的客户端位于相同的地址(使用与服务器不同的端口),它工作正常。

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

采纳答案by user276648

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.

从我读到的(在 Windows 中使用特定网络接口的套接字)绑定在 Vista 之前的 Windows 上仅适用于传入流量,而不会对传出流量执行任何操作。

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

从 Vista 开始是可能的,但默认情况下它不起作用:您需要允许使用“弱主机模型”

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.

有关更多信息,请参阅http://blog.loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/

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).

实际上,与其创建多个环回适配器并更改它们的主机模型,不如创建一个环回适配器,在与真实 IP 不同的网络上为其提供多个 IP 地址,然后仅使用这些 IP 进行测试,这样会更好也更容易. 这样就不会有路由问题,并且您确定一切都在本地(因为在真实适配器和环回适配器之间没有路由)。

回答by Prasanna Bhat

Use below code in the server for binding the connection on all the interface on the same port.

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

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