5

各クライアントが IP アドレスで認識されるサーバー/クライアント アプリケーションをテストするために、いくつかのネットワーク アダプターを作成しました ( .NET で仮想ネットワーク アダプターを作成する方法を参照してください)。192.168.0.10 と 11 の両方がローカル イーサネット アダプタに対応するようになりました (10 は「実際の」アダプタで、11 はループバック アダプタです)。

クライアントは、ソケットが特定のアドレスに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();
            }
        }
    }
}
4

2 に答える 2

8

実際には、これはネットワーク アダプターの構成の問題であり、「弱いホスト モデルと強いホスト モデル」に関係しています。

私が読んだことから ( Windowsのソケットに特定のネットワーク インターフェイスを使用する)、Vista より前の Windows でバインドすると、着信トラフィックに対してのみ機能し、発信トラフィックに対しては何もしません。

Vista 以降では可能ですが、デフォルトでは機能しません。次を使用して「弱いホスト モデル」を許可する必要があります。

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

詳細については、 https://web.archive.org/web/20150402200610/http://blog.loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/を参照してください。

編集

実際には、いくつかのループバック アダプターを作成してそれらのホスト モデルを変更する代わりに、1 つのループバック アダプターを作成し、実際の IP とは異なるネットワーク上で複数の IP アドレスを指定して、それらの IP のみをテストに使用する方がはるかに優れて簡単です。 . そうすれば、ルーティングの問題はなく、すべてがローカルのままであることを確認できます (実際のアダプタとループバック アダプタの間にルーティングがないため)。

于 2012-07-03T03:45:37.813 に答える
6

サーバーで以下のコードを使用して、同じポートのすべてのインターフェイスで接続をバインドします。

// Binds and starts listening.
IPEndPoint myEP = new IPEndPoint(IPAddress.Any, 12345);
于 2012-07-21T07:12:59.457 に答える