質問

listen(fd、backlog)を呼び出した後、ソケットのリッスンを解除することは可能ですか?

編集:自分自身を明確にしないという私の間違い。一時的にソケットをリッスンできないようにしたいです。 close()を呼び出すと、ソケットはM2LS状態のままになり、ソケットを再度開くことができなくなります(さらに悪いことに、悪意のあるプログラムがそのソケットにバインドする可能性があります)

一時的にリスニングを解除することは、このアプリが現在これ以上リクエストを受け付けられないことをアップストリームロードバランサーに通知する方法です(最良の方法ではないかもしれません)

役に立ちましたか?

解決

一部のソケットライブラリを使用すると、着信接続を明確に拒否できます。例: GNU CommonC ++:TCPsocketクラスには 拒否 メソッド。

BSDソケットにはこの機能はありません。ソケットを開いたままで、接続を受け入れ、すぐに閉じることができます:

while (running) {

  int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
  while (noConnectionsPlease) {
    shutdown(i32ConnectFD, 2);
    close(i32ConnectFD);
    break;
  }

}

他のヒント

ソケットを閉じた後、プログラムはまだソケットが「使用中」であると表示する場合があります。これは、私が正確に知らない奇妙さのためです。しかし、ソケットに関するマンページには、「SO_REUSEADDR」と呼ばれる同じソケットを再利用するフラグがあることが示されています。 " setsockopt()"を使用して設定します。

閉じます。思い出すと、

close(fd);

質問の編集したバージョンに基づいて、「聞く」必要があるかどうかわからないまたはclose()。 2つのオプションが思い浮かびます:

1)listen()を呼び出した後、accept()を(論理的に)呼び出すまで、接続は実際には受け入れられません。 「聞く」ことができます;ソケットアクティビティを無視し、準備ができるまでaccept()を延期するだけです。着信接続は、ポートがリッスンモードで開かれたときに作成されたキューにバックログを試みます。スタックでバックログキューがいっぱいになると、それ以降の接続試行は単にフロアにドロップされます。 accepts()で再開すると、バックログをすばやくデキューし、さらに接続できるようになります。

2)ポートを一時的に完全に閉じたように見せたい場合は、カーネルレベルのパケットフィルターをポートに動的に適用して、着信接続の試行がネットワークスタックに到達しないようにすることができます。たとえば、ほとんどの* nixプラットフォームでBerkeley Packet Filter(BPF)を使用できます。つまり、プラットフォームのファイアウォール機能を使用して、目的のポートに着信する受信パケットをドロップします。もちろん、これはプラットフォームによって異なりますが、可能なアプローチです。

アップストリームのロードバランサーに信号を送るのに良い方法だとは思いません。メッセージが通過する前に実際にサーバーにいくつかの接続を送信する必要があります-これらの接続はおそらく拒否されます。

同様に、リスニングソケットを閉じたときに保留中だった接続は、データなしで閉じられます。

アップストリームロードバランサーにシグナルを送信する場合は、そのためのプロトコルが必要です。 TCPを悪用しないでください。

クライアントが通常のWebブラウザーである場合、幸いなことに多くのことを回避できます-単にソケットを閉じるだけで、一般的にユーザーに対して透過的に(ある程度まで)再試行されます。

リスンを解除する明示的な方法はありません!

close(fd)または shutdown(fd、how)のいずれか

fd is the socket file descriptor you want to shutdown, and how is one of the following:

0 Further receives are disallowed

1 Further sends are disallowed

2 Further sends and receives are disallowed (like close())

基本的なレベルでは、ソケットは開いているか閉じています(ここではTCP / IP状態図の良さを無視します)。

ソケットが閉じている場合、何もデータを送信できません。開いている場合、バッファリングアルゴリズムが「十分!」と叫ぶまで、TCP / IPスタックによって着信データが受け入れられ、確認されます。その時点では、それ以上のデータは確認されません。

あなたには2つの選択肢があります。リスニングを解除したいときにソケットをclose()し、後で再び開く-TIME_WAIT2が期限切れになる前によく知られているポートに再バインドできるようにSO_REUSEADDRフラグを指定してsetsockopt()を使用します。

もう1つの選択肢は、ソケットを開いたままにすることです。ただし、「ビジー」になっている間は、単にそこからaccept()しないでください。リクエストに対するアプリケーションレベルの確認応答があると仮定すると、ロードバランサーは応答を得ていないことに気付き、それに応じて行動します。

編集した質問に基づいたややいアプローチを次に示します。

通常のバックログでリッスンするためのソケットを開きます。続行します。

「シャットダウン」したい場合は、バックログが1でSO_REUSEADDRの2つ目を開きます。最初のものを閉じます。再開する準備ができたら、通常のバックログを持つソケットと別のソケットをやり取りします。

ここで、閉じているソケットから受け入れキューを空にすることに関する注意深い詳細がここでのキラーになります。おそらく、このアプローチを実行不可能にするのに十分なキラーです。

必ずしもこれが良い考えだとは思いませんが、...

listenをもう一度呼び出すことができます。 POSIX仕様はそうしないと言っていません。おそらく、「アンリスン」したいときに、バックログパラメータを0にしてもう一度呼び出すことができます。

listenが0のbacklogで呼び出されたときに何が起こるかは、実装定義のようです。 POSIX仕様では、接続を 許可できると記載されています。これは、バックログパラメーターが0の場合、一部の実装がすべての接続を拒否することを意味します。 0(おそらく1またはSOMAXCONNのいずれか)。

質問では、どのような種類のソケットかは言われませんでした。 UNIXソケットの場合、rename(2)を使用してリスニングを停止および開始できます。 unlink(2)でリッスンを完全に停止することもできます。ソケットは開いたままなので、バックログのサービスを継続できます。このアプローチは非常に便利に思えますが、以前は使用したことがなく、自分で調べているだけです。

ソケットAPIを介してこれを実行することは不可能であるという回答がすでにあります。

他のOSメソッド(つまり、ホストfirwewall / iptables / ipfilter)を使用して、一時的な拒否ルールを設定できます。

ほとんどのロードバランサーは、接続の問題を認識するために提供する可能性が少し制限されていることがわかりました(ほとんどのロードバランサーは、正当な接続試行に対する回答としてではなく、接続プローブでのみRSTを認識します)。

とにかく、利用できないことを検出するプローブによって制限されている場合、HTTP要求またはFTPログインを行うアプリケーションレベルのプローブをセットアップするか、受け入れた後に単純に閉じる場合に認識される類似のものをセットアップします。 「500 service not available」などのエラーメッセージを解釈することもできますが、とにかくすっきりしています。 SNMPを使用すると、一部のロードバランサーは結果をロードヒントとして使用することもできます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top