私は、多数の「プレーヤー」を制御する必要がある「サーバー」を備えたLANベースのソリューションに取り組んでいます。選択したプロトコルはUDPです。これは、接続が必要なく、トラフィックが短いコマンドのみで構成されているためです。同期にはブロードキャストメッセージを、プレーヤーの個々のコマンドには単一のターゲットメッセージを組み合わせて使用したいと思います。
マルチキャストTCPは代替手段ですが、より複雑で、タスクに正確に適していないため、ハードウェアで十分にサポートされていないことがよくあります。
残念ながら、私は奇妙な問題に遭遇しています:
「sendto」を使用して特定のIPに送信された最初のデータグラムは失われます。 その後すぐに同じIPに送信されたデータグラムが受信されます。しかし、しばらく(数分)待つと、最初の「送信」が再び失われます。
ブロードキャストデータグラムは常に機能します。(同じコンピューターへの)ローカル送信は常に機能します。
オペレーティングシステムまたはルーター/スイッチには、IPアドレスからMACアドレスへの変換テーブルがあり、数分間使用しないと忘れられてしまい、残念ながらデータグラムが失われると思います。さまざまなルーター/スイッチハードウェアでその動作を観察できたので、私の疑いはWindowsネットワーク層です。
UDPは定義上「信頼性が低い」ことは知っていますが、物理的な接続が機能していて、すべてが明確に定義されている場合でも、パケットが失われる可能性があるとは信じられません。そうすれば、それは文字通り価値がないでしょう。
技術的には、UDPソケットを開いて、ポートとINADRR_ANYにバインドします。次に、「sendto」と「recvfrom」を使用しています。私は決して接続をしません-私は何人かのプレーヤーを持っているので私は接続したくありません。私の知る限り、UDPは接続しなくても機能するはずです。
私の現在の回避策は、ダミーのデータグラムをすべての特定のプレーヤーのIPに定期的に送信することです。これで問題は解決しますが、どういうわけか「満足のいくものではありません」
質問:誰かがその問題を知っていますか?それはどこから来たのですか?どうすれば解決できますか?
編集:
私はそれを次のテストプログラムに要約しました:
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET Sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SOCKADDR_IN Local = {0};
Local.sin_family = AF_INET;
Local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
Local.sin_port = htons(1234);
bind(Sock, (SOCKADDR*)&Local, sizeof(Local));
printf("Press any key to send...\n");
int Ret, i = 0;
char Buf[4096];
SOCKADDR_IN Remote = {0};
Remote.sin_family = AF_INET;
Remote.sin_addr.S_un.S_addr = inet_addr("192.168.1.12"); // Replace this with a valid LAN IP which is not the hosts one
Remote.sin_port = htons(1235);
while(true) {
_getch();
sprintf(Buf, "ping %d", ++i);
printf("Multiple sending \"%s\"\n", Buf);
// Ret = connect(Sock, (SOCKADDR*)&Remote, sizeof(Remote));
// if (Ret == SOCKET_ERROR) printf("Connect Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
}
return 0;
プログラムはUDPソケットを開き、キーストロークごとに3つのデータグラムを連続して特定のIPに送信します。WiresharkがUDPトラフィックを監視しながら実行し、キーを押し、しばらく待ってからもう一度キーを押します。リモートIPに受信機は必要ありません。黒でマークされた「到達不能」パケットを取得しないことを除いて、違いはありません。これはあなたが得るものです:
ご覧のとおり、最初の送信でIPのARP検索が開始されました。その検索が保留されている間、3つの連続した送信の最初の2つが失われました。2番目のキーストローク(IP検索が完了した後)は、3つのメッセージを適切に送信しました。これでメッセージの送信を繰り返すことができ、待つまで(アドレスの翻訳が再び失われるまで約1分)機能し、その後再びドロップアウトが表示されます。
つまり、UDPメッセージを送信するときに送信バッファがなく、保留中のARP要求があります。最後のメッセージを除いて、すべてのメッセージが失われます。また、「sendto」は正常に配信されるまでブロックされず、エラーは返されません。
それは私を驚かせ、少し悲しくします。それは、現在の回避策を実行するか、一度に1つのメッセージのみを送信して応答を待つACKシステムを実装する必要があることを意味します。これは簡単ではありません。より多くの、そして多くの困難を意味します。