web-dev-qa-db-ja.com

同じコンピューター上の2つのプログラム間でUDPパケットを送受信する

同じポート番号を共有することにより、localhost/127 ...を介してUDPを介して同じコンピューター(片方向のみ)で通信する2つの別個のプログラムを取得することは可能ですか?

私たちは、2つのコンピューター間でテレメトリーを含むUDPパケットを送信する必要がある学生プロジェクトに取り組んでいます。これらのパケットを生成するプログラムはプロプライエタリですが、C#でSystem.Net.Sockets.UdpClientおよびSystem.Net.IPEndPointを使用して受信プログラムで作業しています。

これは、2つのプログラムを個別に実行できる複数のコンピューターが接続されているグループの会議中に正常に機能します。しかし、自宅にいてテレメトリ処理プログラムを拡張しようとすると、コンピューターが1台しかないため、あまり役に立ちません(処理プログラムをテストするためのフィードが必要です)。学校のどのコンピューターにもプログラムをインストールできません。

コンピューター上で両方のプログラムを同時に実行しようとすると(プログラムを最後に開始する)、SocketExceptionが発生し、各ポートの1回の使用のみ通常が許可されます。これにより、ポートを共有する何らかの方法が必要になります(一度に1つのプログラムのみがコンピューターのポートを使用できることは理にかなっていますが、複数のインターネットブラウザーを同時に実行するのに問題はありません(と私はhttp)にポート80を使用するとします)。

編集の再編集:

sipwizは正しかったです。UdpClient.Client.Bind()へのポインターを提供してくれたKalmiに感謝します。ただし、現時点では、同様のパケットを生成する別のプログラムを使用することを検討しています。これにより、最初の(素朴ではありますが)アクターのUDPクライアントバインディングを使用して、同じコンピューター上のポートを共有できます。回答のマークを解除する必要があることを残念に思う、sysrqb。

35

これが可能になるとは思っていませんでしたが、..... sipwizは正しかったです。

とても簡単にできます。 (sipwizの回答を投票してください!)

IPEndPoint localpt = new IPEndPoint(IPAddress.Any, 6000);

//Failed try
    try
    {
        var u = new UdpClient(5000);
        u.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        UdpClient u2 = new UdpClient(5000);//KABOOM
        u2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    }
    catch (Exception)
    {
        Console.WriteLine("ERROR! You must call Bind only after setting SocketOptionName.ReuseAddress. \n And you must not pass any parameter to UdpClient's constructor or it will call Bind.");
    }

//This is how you do it (kudos to sipwiz)
    UdpClient udpServer = new UdpClient(localpt); //This is what the proprietary(see question) sender would do (nothing special) 

    //!!! The following 3 lines is what the poster needs...(and the definition of localpt (of course))
    UdpClient udpServer2 = new UdpClient();
    udpServer2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    udpServer2.Client.Bind(localpt);
35
Tarnay Kálmán

ReuseAddressソケットオプションを使用して、ポートに複数回バインドできます。

UdpClient udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

UDPサーバーソケットにも同じオプションを設定する必要があります。

76
sipwiz

TarnayKálmánとsipwizによる回答からの完全なコードは次のとおりです。

サーバーコード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UdpBroadcastTest
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Sender");
            // This constructor arbitrarily assigns the local port number.

            UdpClient udpClient = new UdpClient();
            udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            udpClient.Connect("localhost", 11000);
            try
            {
                string message = String.Empty;
                do
                {
                    message = Console.ReadLine();
                    // Sends a message to the Host to which you have connected.
                    Byte[] sendBytes = Encoding.ASCII.GetBytes(message);

                    udpClient.Send(sendBytes, sendBytes.Length);
                } while (message != String.Empty);

                udpClient.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            Console.WriteLine("Press Any Key to Continue");
            Console.ReadKey();
        }
    }
}

クライアントコード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UdpReciever
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Receiver");
            // This constructor arbitrarily assigns the local port number.
            UdpClient udpClient = new UdpClient();
            udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 11000));
            try
            {
                //IPEndPoint object will allow us to read datagrams sent from any source.
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

                string message = String.Empty;
                do
                {

                    // Blocks until a message returns on this socket from a remote Host.
                    Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                    message = Encoding.ASCII.GetString(receiveBytes);

                    // Uses the IPEndPoint object to determine which of these two hosts responded.
                    Console.WriteLine("This is the message you received: " +
                                                 message);
                    //Console.WriteLine("This message was sent from " +
                    //                            RemoteIpEndPoint.Address.ToString() +
                    //                            " on their port number " +
                    //                            RemoteIpEndPoint.Port.ToString());
                }
                while (message != "exit");
                udpClient.Close();
                //udpClientB.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            Console.WriteLine("Press Any Key to Continue");
            Console.ReadKey();
        }
    }
}
5
epotter

ネットワークカードに複数のIPアドレスを配置したり、ループバックしたり、サーバーとクライアントを異なるIPアドレスにバインドしたりできますか?

それ以外の場合は、仮想マシンのアプローチが確実に機能します。

2
Douglas Leeder

1つのプログラムだけがバインドを一度にポートにできます。複数のプログラムが別のシステムの1つのポートにconnectできますが、異なるWebブラウザがバインドしているローカルポートはランダムに割り当てられます。

Someいプロセス間通信やパケットスニッフィングを行う場合を除き、複数のプログラムを1つのポートにバインドする方法はありません。

1
rmmh

私のアドバイス:ポート番号をUdpClientコンストラクターに渡さないでください。 ドキュメント から(ややまばらです、知っています...)、そうすると、UdpClientはそのポートにbindを試みます(sysrqbが言及したように) 、許可されていません)。 (そうしない場合、UdpClientはランダムなポートで応答をリッスンすると信じています。使用されていないことがわかっているポートを選択することもできます。)

Connect()を呼び出すときは、サーバーがlistening onであるポート番号を渡す必要があります。

0
mpontillo

IPアドレスを渡すことができるようにコードを変更しても、同じポートにバインドできず、ここで使用できるポートは1つだけであるように見える同じエラーメッセージが表示されますローカルマシンからIPをキャプチャします。IPAddress ipAddress = Dns.Resolve(Dns.GetHostName())。AddressList [0]; IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress、11000);

        //IPEndPoint localpt = new IPEndPoint(ipLocalEndPoint);

        UdpClient udpServer = new UdpClient(ipLocalEndPoint);
        udpServer.Client.SetSocketOption(
            SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpServer.Connect(ipLocalEndPoint);
        UdpClient udpServer2 = new UdpClient();
        udpServer2.Client.SetSocketOption(
            SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        udpServer2.Client.Bind(ipLocalEndPoint); // <<---------- Exception here

これにより、Bind()メソッドで例外が発生します。

0
MethodMan

2つのプログラム、つまり送信者と受信者をlocalhost.datsの同じポートにバインドします。簡単な答え。

0
saikat