12

私はこの小さなサーバー アプリケーションをピュア C で作成しました。これは、特定のポートで着信接続をリッスンする、非常に単純なものです。

通常のソケット初期化手順に従い、ポートに を作成し、socket()それを a と言い、への着信接続を待機して無限にループします。bind()listen()select()accept()

すべてがうまくいき、魔法のように機能しますが、数か月間実行したままにしておくと、アプリケーションサーバーがそれに気付かずに実行し続けている間、リッスンポートが閉じます。言われていない。

問題は、アプリケーションが気にせずにポートが閉じられているのはなぜですか? また、それを防ぐにはどうすればよいでしょうか?

それは期待される動作ですか?何らかの例外をチェックするか、必要に応じてリッスンしているソケットで「ヘルスチェック」を行う必要がありますか?

コード: https://gist.github.com/Havenard/e930be035a3bee75c018 (はい、私は0エラーの手がかりとして使用していることに気付きました。それは悪い練習などですが、コメントで説明したように、質問には関係ありません。ソケットファイル記述子を設定して0、ループを停止し、アプリケーションをシャットダウンします)。

4

5 に答える 5

6

私はそれをきれいにすることから始めます:

  • より小さく、読みやすく、検証可能で、テスト可能な関数に分割します
  • リンクされたリストの使用法は乱雑に見えます。おそらくいくつかの汎用関数を導入することで、大幅に簡素化できます。
  • すべてのばかげた '\x20' 文字定数を、より読みやすい同等の ' ' 定数に置き換えます
  • ここのような明示的な魔法の定数は避けてくださいif (n_case > 0) memcpy(nick, node->nick, (n_case > 32 ? 32 : n_case));。sizeof はあなたの友達です。
  • 未使用のファイル記述子のセンチネル値としてゼロを使用しないでください。代わりに -1 を使用してください。
  • サイズとインデックスに符号なしの型を使用します。負のインデックスはメモリを破壊し、符号のない型を折りたたむとすぐに失敗します。(フェイルファストはあなたの友達です)

たった数時間の編集です。

私の推測では、クリーンアップ/リファクタリングの後、「バグ」が魔法のように表面化するでしょう。

脚注: いいえ、私はあなたの代わりにあなたの仕事をしません. 100ポイントでも1000ポイントでもありません。自分で片付けてください。

于 2013-03-06T19:40:42.847 に答える
2

この回答は、ほとんどの場合、 を呼び出す場所のコード レビューですclose()

行 330: ソケットを閉じますが、コード内の他の場所のようにすぐに続行しません。これにより、奇妙な動作が発生する可能性があります。

行 928: ほとんどの場所では、 へ0の呼び出しの後にクライアントまたはサーバー ソケットを に設定しますclose()。あなたはこの電話の後ではありません。

1193行目:928行目と同じコメントです。

1195行目:928行目と同じコメントです。

1218行目:928行目と同じコメント。

1234行目:928行目と同じコメントです。

1236行目:928行目と同じコメント。

完全な警告を表示してコードをコンパイルしたところ、関数が値を返すように宣言されているにもかかわらず、値が返されていないことをコンパイラが指摘している箇所がいくつかありました。

x.c:582: warning: no return statement in function returning non-void
x.c:591: warning: no return statement in function returning non-void
x.c:598: warning: no return statement in function returning non-void
x.c:609: warning: no return statement in function returning non-void
x.c:620: warning: no return statement in function returning non-void
x.c:728: warning: no return statement in function returning non-void
x.c:779: warning: no return statement in function returning non-void

他の投稿で指摘されているように、他にも多くの問題があります。

この問題をデバッグする限り、バインディング ソケットが早期に閉じられている疑いがある場合は、閉じclose()られている記述子がバインディング ソケットと一致しないことを主張する独自のバージョンで呼び出しをインターセプトします。

ただし、wildplasser が指摘したように、select()記述子が閉じられている場合、無効な記述子に関するエラーが返されます。

于 2013-03-12T21:25:36.720 に答える
1

バグは、無効なファイル記述子として 0 を使用していることです。0 は完全に有効で、通常は stdin です。次に、シグナル ハンドラーでリスナーが 0 に設定されます。次に、fd として 0 を使用し、ある時点でいくつかのソケットで close(0) を実行すると、0 をチェックせずに close(fd) を実行し、リスナーを効果的に閉じるブランチがあります。リスナーの動作を停止するもう 1 つのオプションは、バックログをオーバーフローさせることです。

もう 1 つの問題 - fds に unsigned int を使用しています。システム コールはエラー時に -1 を返します ... unsigned int struct identd_node -> unsigned int handle; に割り当てられている場合、そのエラーは検出されません。struct thread_node -> unsigned int skt_clnt, skt_serv;

于 2013-03-10T22:27:37.687 に答える
0

システムは説明どおりに動作するべきではありませんが、動作する場合があります。サーバーシステムの場合、通常、外部(スクリプトから)またはコード内の特別なスレッドからヘルスチェック呼び出しを実行する必要があります。

したがって、数回の連続試行でサーバーに接続できないことが検出された場合(過負荷状態の可能性があるため、ほとんど必要ありません)、ソケットが壊れていると見なして再作成するか、サーバーを再起動できます。

于 2013-03-13T17:32:57.563 に答える
0

エラーが発生するには、コードに 2 つの連続したエラーが必要なようです。

select からエラーが発生した場合は、なぜすぐに出力しないのでしょうか?

281 行目で printf errno/perror を実行して、問題を特定します。

于 2013-03-07T12:50:11.927 に答える