Sender が UDP データグラムを Service に送信し、受信データグラムのソース アドレスとソース ポートを使用して応答データグラムを返す基本的な UDP プロトコルを実装しようとしています。
通常、送信者もそのポートで応答をリッスンします。しかし、そのホストで実行されている別のプログラム (リスナー) によって応答が取得されるようにしたいと考えています。そう:
- ホスト A では、Listener が開始され、ポート 12345 にバインドされ、でブロックされ
recvfrom
ます。 - ホスト A では、送信元アドレスとポートをホスト A のポート 12345 に設定して、ホスト B で実行されているサービスにデータグラムを送信します。
- ホスト B のサービスは、ホスト A のポート 12345 に応答を送信します。
- 応答はリスナーによって取得されます。
送信元アドレスとポートの設定は、それらにバインドすることによって行われます。したがって、同じポートにバインドするには、Sender と Listener の両方が必要です。両方で SO_REUSEADDR を設定すると、これが可能になります。ここではマルチキャストを使用していないことに注意してください。
しかし、応答はリスナーによって確実に取得されていません。私が観察した2つの例外があります。
Sender が最初のデータグラムを送信した直後にソケットを閉じると、応答が Listener に届くことがわかりました。
または、Sender が最初に開始され、Listener の前にバインドされている場合、応答は Listener によって取得されます。
私はインターネットの例に取り組んできましたが、何が起こるべきかを明確に説明しているドキュメントを見つけていません。しかし、私が見たいくつかの場所では、ユニキャストの場合、ポートにバインドされた最新のプロセスのみが送信されたデータグラムを受信することを示唆しています。
私の質問は、応答 (送信元アドレスとポートを使用して送信された) が別のプロセスによって取得されるように、UDP データグラムを送信できますか? 上記のプロセスが機能しない場合、そのポートにバインドせずに発信データグラムにソース情報を設定する方法はありますか?
その他のいくつかのポイント:
- 各プロセスは独立して開始され、他のプロセスに干渉することなく再起動できる必要があります。したがって、ソケットを開いてもう一方を生成することはできないと思います。
- 両方のプロセスからパケットを受信する必要はありません。1 つのプロセスは送信のみで、もう 1 つのプロセスは受信のみです。
- 理想的には、このソリューションは、一般的な Unix や Windows で実行できる移植性を備えている必要があります。
- 最後に、それが不可能な場合は、1 つのプロセスを使用して両方の機能を実行する方法に戻ります。あまりストレスを感じていませんが、できることならやってみたいと思っています。:-)
ネットワーク コードは次のとおりです...
送信者コード
void run(Options *options)
{
struct sockaddr_in si_me, si_other;
int s;
socklen_t slen = sizeof(si_other);
int reuse = 1;
struct hostent *he;
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
die("socket");
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
die("setsockopt");
// Bind to the "listen port", so that outgoing datagrams have the correct source information
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(options->listen_port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) != 0)
die("bind");
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(options->service_port);
if (!(he = gethostbyname2(options->service_host, AF_INET)))
die("gethostbyname2");
memmove(&si_other.sin_addr.s_addr, he->h_addr, he->h_length);
while (1)
{
int len;
char *buf;
// Create outgoing message in buf
...
if (sendto(s, buf, len, 0, (struct sockaddr *) &si_other, slen) == -1)
die("sendto");
}
close(s);
}
リスナーコード
static void run(Options *options)
{
struct sockaddr_in si_me, si_other;
int s;
socklen_t slen = sizeof(si_other);
char buf[BUFLEN];
int reuse = 1;
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
die("socket");
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
die("setsockopt");
// Bind to the same "listen port" to pick up responses to datagrams sent by Sender
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(options->listen_port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) == -1)
die("bind");
while (1)
{
int nr;
nr = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen);
if (nr == -1)
die("recvfrom");
// Process the received message
...
}
close(s);
}
関連する質問はUsing netcat to send a UDP packet without bindingです。1 つの回答は、SO_SOCKADDR を使用して可能であることを示唆しているようですが、私の場合にどのように機能するかについては十分に説明していません。