sendto() を使用して UDP データを複数のポート (すべてsocket(PF_INET, SOCK_DGRAM, 0) で開かれている) に書き込んでいるアプリで、一連のクライアント読み取りプロセスのために説明できない動作が発生しています。これらの sendto() は、ときどき予期せずにECONNREFUSEDエラーを引き起こします。これは macOS Sierra (10.12) システムで発生しており、sendto(2) のマニュアル ページにはECONNREFUSEDが考えられるエラーとしてリストされていません。興味深いことに、私は CentOS7 システム (これらのエラーは決して発生しません) を持っており、その sendto(2) マニュアル ページは udp(7) マニュアル ページに記載されている追加の sendto() エラーを参照しており、その CentOS7 udp(7) ページには次のように書かれています:
サービス拒否
No receiver was associated with the destination address. This
might be caused by a previous packet sent over the socket.
( ECONNREFUSEDは、macOS Sierra udp(4) ページのどこにも言及されていません。) CentOS7 のマニュアル ページが macOS と関連があるかどうかはわかりませんが、関連があると仮定すると、sendto に関するECONNREFUSEDの上記の説明は() はいくつかの点で混乱しています:
まず、UDP について私がこれまでに耳にしたことはすべて、そのコネクションレスな性質を強調しています。では、レシーバーが接続されていない (または、man ページにあるように「関連付けられている」と、私は同じことを意味します) ため、sendto() が失敗するのはなぜでしょうか? UDP の要点は、あなたが話し手である場合、他の誰かが聞いているかどうかを気にせずにしゃべるだけではないでしょうか? ただし、これらの CentOS7 udp(7) コメントは私の Sierra システムに当てはまるようです。これらのポートにバインドされており、これらのポートから読み取るクライアント プロセスが実行されている場合、問題が発生することはありませんが、リーダーよりも前に UDP ライターを起動すると、実行しているときに、これらのエラーが頻繁に表示されます (常にではありません)。
第 2 に、CentOS7 udp(7) のドキュメントによると、ソケット経由で送信された以前のパケットによって受信者が宛先アドレスに関連付けられない理由を説明できる人はいますか? これは私にはまったく意味がありません。一部のデータグラムは非常に有毒で、それを読んだ人を殺しますか?
また、CentOS7 でこの問題が実際に (漠然とではあるが) 文書化されているのを見たことがないことに加えて、Sierra より前の MacOS リリースでもこの問題を経験したことがなく、このコードは何年もの間うまく動作していることにも注意してください。 . 私はまだ El Capitan システムを 1 つ持っていますが、そこでエラーを再現することはできません。
以下は、私のアプリに関する詳細情報です。PF_INET UDP、sendto()、およびECONNREFUSEDに関する上記の一般的な質問、または以下に示す私のアプリのより具体的な詳細について、お気軽にコメントしてください。使用可能な回避策は既にありますが (以下を参照)、何が起こっているのかをよりよく理解したいと考えています。
私のアプリは、さまざまなソース (シリアル回線および/または UDP ポート) からデータを読み取り、それをさまざまなタイプの再フォーマットされた出力メッセージに変換してから、それらのメッセージを複数の事前定義された連続番号 (3000 から 3004 など) の UDP ポートに書き込みます。少数の可変数のクライアント (5 に制限されますが、通常は 3 または 4 以下) によって読み取られる同じ IP アドレス。各クライアントは、アプリの UDP 出力ポートの定義済みリストをスキャンし、最初に使用可能なポートにバインドしてから、そのポートからすべての読み取りを行います。ライター アプリと複数のリーダー プロセスが開始される順序を前もって保証することはできません (ここでの問題の中心部分)。私のアプリは、通常はそれぞれ約 80 バイト以下の各出力ポート (すべて ASCII テキスト) に約 1 秒に 1 回メッセージを書き込んでいます。
これらのリーダー クライアントは、(i) アプリと同じローカル ホスト、(ii) 単一のリモート ホスト、または (iii) ローカル ネットワーク上の異なるリモート ホストで実行されている可能性があるため、ライター アプリは任意の IPv4 宛先アドレスをコマンド引数。ライターがホスト 192.168.1.LLL (ローカル ホスト) で実行されていると仮定すると、最も一般的に使用される宛先アドレスは次のようになります。
- 127.0.0.1
- 192.168.1.LLL (ローカルホストの実際の外部アドレス)
- 192.168.1.RRR (同じ LAN 上のリモート ホスト)
- 192.168.1.255 (複数のリモート ホスト上のリーダーのローカル ブロードキャスト)
出力を 127.0.0.1 または 192.168.1.LLL (localhost の実際の外部アドレス) に送信する場合にのみ、これらのエラーが表示されることに注意してください。特定のリモート ホスト 192.168.1.RRR または LAN のブロードキャスト アドレス 192.168.1.255 に書き込むと、エラーは発生しません。ローカルの PF_INET とリモートの PF_INET UDP 書き込みで何が起こるかに違いがあるはずですか? ローカル書き込みは、さまざまな制約を受けるローカル バッファ内で特定の方法で処理する必要があるかもしれませんが、ホスト外に送信されたパケットは風にまき散らされ、何が起こってもローカル sendto() のレポート容量を超えていると見なされますか? ブロードキャスト アドレス 192.168.1.
今のところ、すべてのECONNREFUSEDを無視することで、この問題を回避しています。sendto() エラー。アプリを起動してから数秒以内にそれらを取得する傾向があるようですが、各ポートの最初の sendto() ではなく、通常は 5 つの出力ポートの 1 つだけです (ただし、エラーを生成するポートは常に同じではありません) )。そして、最初のエラーの後、リーダーがまだ実行されていないにもかかわらず、次の数分間の出力 (私がこれまでに見た中で最も長い出力) にはエラーがありません。ただし、これらのエラーは不可解であり、コードを可能な限り堅牢にするために、エラーをよりよく理解したいと考えています。後者はすでに長すぎるため、この投稿には実際のコードは含めませんが、私が知る限り、コードに異常はありませんが、それが役立つ場合は別に投稿できます.
ありがとう!
ロジャー・デイビス、大学。ハワイの