私は、20スレッドを使用して、多数のリクエスト(1つのノードで最大500 /秒)を処理するマルチスレッドサーバー(スレッドプール)を持っています。着信接続を受け入れ、ハンドラスレッドが処理するためにそれらをキューに入れるリスナスレッドがあります。応答が準備できたら、スレッドはクライアントに書き込み、ソケットを閉じます。最近まで、全てがうまくいっていたようで、テストクライアントプログラムは応答を読んだ後にランダムにぶら下がり始めました。掘り起こすと、サーバからのclose()は実際にソケットを切断していないようです。ファイルディスクリプタ番号のコードにデバッグ用のプリントを追加しました。このタイプの出力が得られます。close()がソケットを正しく閉じていない
Processing request for 21
Writing to 21
Closing 21
close()の戻り値は0で、別のデバッグステートメントが出力されます。この出力がハングしたクライアントでは、lsofは確立された接続を示しています。
のIPv4 32754237 TCP localhostを21U、SERVER 8160のルート:9980->はlocalhost:47530(ESTABLISHED)のIPv4 32754228 TCP localhostを12U
CLIENT 17747ルート:47530->はlocalhost:9980(ESTABLISHED)
は、それはようですサーバーがシャットダウンシーケンスをクライアントに送信しないで、クライアントが強制終了状態になるまでこの状態がハングします。
SERVER 8160ルート21u IPv4 32754237 TCP localhost:9980-> localhost:47530 (CLOSE_WAIT)
また、クライアントのタイムアウトが指定されている場合は、ハングするのではなくタイムアウトになります。私も手動で実行することができます
call close(21)
gdbからサーバに接続すると、クライアントは切断されます。おそらく50,000回のリクエストで1回発生しますが、長期間は発生しない可能性があります。
Linuxバージョン:2.6.21.7-2.fc8xen CentOSにバージョン:次のように5.4(最終)
ソケットアクションがある
SERVER:
int型client_socket。 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr);
while(true) {
client_socket = accept(incoming_socket, (struct sockaddr *)&client_addr, &client_len);
if (client_socket == -1)
continue;
/* insert into queue here for threads to process */
}
スレッドは、ソケットをピックアップして応答を作成します。
/* get client_socket from queue */
/* processing request here */
/* now set to blocking for write; was previously set to non-blocking for reading */
int flags = fcntl(client_socket, F_GETFL);
if (flags < 0)
abort();
if (fcntl(client_socket, F_SETFL, flags|O_NONBLOCK) < 0)
abort();
server_write(client_socket, response_buf, response_length);
server_close(client_socket);
server_writeとserver_close。
void server_write(int fd, char const *buf, ssize_t len) {
printf("Writing to %d\n", fd);
while(len > 0) {
ssize_t n = write(fd, buf, len);
if(n <= 0)
return;// I don't really care what error happened, we'll just drop the connection
len -= n;
buf += n;
}
}
void server_close(int fd) {
for(uint32_t i=0; i<10; i++) {
int n = close(fd);
if(!n) {//closed successfully
return;
}
usleep(100);
}
printf("Close failed for %d\n", fd);
}
CLIENT:
クライアント側はlibcurlののVに空想7.27.0
CURL *curl = curl_easy_init();
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, write_tag);
res = curl_easy_perform(curl);
何も、単に基本的なカール接続を使用していません。ソケットが閉じられていると認識されないため、(libcurlの)tranfer.cでクライアントがハングします。それはサーバーからのより多くのデータを待っています。
物事私がこれまで試した:
シャットダウン近い
shutdown(fd, SHUT_WR);
char buf[64];
while(read(fd, buf, 64) > 0);
/* then close */
前には差が行われていない1秒
struct linger l;
l.l_onoff = 1;
l.l_linger = 1;
if (setsockopt(client_socket, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
abort();
これらの中で強制的に閉じるようにSO_LINGERを設定します。どんなアイデアでも大歓迎です。
EDIT - これは、キューライブラリ内のスレッドセーフの問題であり、ソケットが複数のスレッドによって不適切に処理されてしまいました。ここで
あなたは100%肯定的ですが、他のスレッドは、そのソケットで 'close'を呼び出すときにソケットを使用している可能性がありますか?ノンブロッキングの読み込みはどのように行いますか? –
私はちょうどここにログインし、この問題を覚えていますか?私は後で、接続を渡すために使用されるキューにスレッドセーフティの問題があることを知りました。ここにバグはありませんでした。誤った情報を申し訳ありません。 – DavidMFrey