2012-05-25 15 views
5

私はクライアントとサーバーにブロッキングTCPソケットを使用しています。私が読むたびに、私はまず、selectを使ってストリーム上でデータが利用可能かどうかをチェックします。私は常に40バイトずつ読み書きします。ほとんどの読み取りには数ミリ秒かそれ以下の時間がかかりますが、半分以上の時間がかかります。ソケット上で利用可能なデータがあることを知った後。非常に遅いソケット読み取りの原因は何ですか?

私もそれを引き起こしている可能性が何TCP_NODELAY

を使用していますか?

EDIT 2

私が送信され、受信した各パケットのタイムスタンプを分析し、クライアントは次のオブジェクトがサーバーによって書き込まれる前に、オブジェクトを読み取ろうとする場合にのみ、この遅延が起こることを見ました。例えば、サーバはオブジェクト番号xを書いた後、サーバはオブジェクト番号x + 1の書込みを開始する前にオブジェクトxを読み取ろうとした。これにより、サーバー側で何らかの合体が起こっていると思われます。

EDIT

サーバーは、3つの異なるポートでリッスンしています。クライアントは、これらの各ポートに1つずつ接続します。

3つの接続があります。サーバーからクライアントにデータを頻繁に送信する接続です。もう1つは、クライアントからサーバーにデータを送信するだけです。また、1バイトのデータを送信することはめったにありません。私は最初の接続の問題に直面しています。私はselect()を使用してデータがその接続で利用可能であることを確認しています。そして、40バイトの読み取りをタイムスタンプすると、その読み取りに約0.5秒かかることがわかります。

これは、Linux上のgccを使用して

非常に参考になるプロファイルにどのようにのように任意のポインタ。

rdrr_server_start(void) {
int rr_sd; int input_sd; int ack_sd; int fp_sd;

startTcpServer(&rr_sd, remote_rr_port); startTcpServer(&input_sd, remote_input_port); startTcpServer(&ack_sd, remote_ack_port); startTcpServer(&fp_sd, remote_fp_port);

connFD_rr = getTcpConnection(rr_sd); connFD_input = getTcpConnection(input_sd); connFD_ack= getTcpConnection(ack_sd); connFD_fp=getTcpConnection(fp_sd); }

static int getTcpConnection(int sd) { socklen_t l en;
struct sockaddr_in clientAddress; len = sizeof(clientAddress); int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len); nodelay(connFD); fflush(stdout); return connFD; }

static void startTcpServer(int *sd, const int port) { *sd= socket(AF_INET, SOCK_STREAM, 0); ASSERT(*sd>0);

// Set socket option so that port can be reused int enable = 1; setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = port; a.sin_addr.s_addr = INADDR_ANY; int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a)); ASSERT(bindResult ==0); listen(*sd,2); } static void nodelay(int fd) { int flag=1; ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); }

startTcpClient() { connFD_rr = socket(AF_INET, SOCK_STREAM, 0); connFD_input = socket(AF_INET, SOCK_STREAM, 0); connFD_ack = socket(AF_INET, SOCK_STREAM, 0); connFD_fp= socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = remote_rr_port; a.sin_addr.s_addr = inet_addr(remote_server_ip);

int CONNECT_TO_SERVER= connect(connFD_rr, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_input_port; CONNECT_TO_SERVER= connect(connFD_input, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_ack_port; CONNECT_TO_SERVER= connect(connFD_ack, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_fp_port; CONNECT_TO_SERVER= connect(connFD_fp, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

nodelay(connFD_rr); nodelay(connFD_input); nodelay(connFD_ack); nodelay(connFD_fp); }

+0

にデータを移動する前に、TCPセグメント... – Jah

+0

**私たちはどのようなサイズ、**サーバからクライアントに頻繁にいくつかのデータを送信しますワンについて話す? – tuxuday

+0

おそらく、TCP_NODELAY(Nagleを無効にする)は悪い選択であり、結果として多くの短いセグメントが送信され、「論理」パケットごとに複数回のラウンドトリップが発生します。アプリケーションプログラムの側面には多くのシステムコールがあります。 – wildplasser

答えて

0

一つのクライアントと複数の接続?

いくつかのソケット関数が実行をブロックしている可能性があります(つまり、関数の結果を待っている)。私は各接続のために(サーバー側で)新しいスレッドを開くことをお勧めしますので、お互いに干渉しません...

しかし、私は暗闇の中で撮影しています。あなたはいくつかの追加情報を送る必要があります...

+0

私は私の質問にいくつかの詳細を追加しました。 – AnkurVj

+0

あなたの問題があるかどうかは分かりませんが、おそらくstartTcpServer/getTcpConnection関数を再配置する必要があります。 startTcpServerにはlisten()の呼び出しがあり、新しい接続が行われるまでプログラムの実行をブロックします。その後、新しい接続を受け入れる必要がありますが、startTcpServer =>別のlisten(同じスレッド上にあります)の別の呼び出しがあります。 ソケットは受信接続を受け入れる代わりに(ただし別の関数で) ... おそらく、次のように並べ替えます。 startTcpServer(); getTcpConnection(); startTcpServer(); getTcpConnection(); ? – Ivica

+0

私は 'listen()'ブロックとは思わない? IMOは 'accept()'だけです! – AnkurVj

0

あなたの声明はまだ "1つのクライアントとの複数のTCP接続"を混乱させています。明らかに、1つのポートでリッスンする単一のサーバーがあります。これで、複数の接続がある場合は、複数のクライアントがサーバーに接続し、それぞれが異なるTCPクライアントポートに接続されていることを意味します。現在、サーバーは選択して、クライアントがデータを持っているものに応答します(つまり、クライアントはソケット上のデータを送信しました)。現在、2つのクライアントが同時にデータを送信すると、サーバーはそれらを順番にしか処理できません。したがって、サーバーが最初に処理を終えるまで、2番目のクライアントは処理されません。

Selectは、サーバーが複数のディスクリプタ(ソケット)とプロセスを監視できるようにします。並行して処理するようなものではありません。そのためには、複数のスレッドまたはプロセスが必要です。

+0

実際には、サーバーは同じクライアントからの3つの異なるポートで接続を受け入れました。理想的には、各接続は他の接続と完全に分離されている必要があります。 – AnkurVj

+0

3つの異なるサーバーソケットの作成を示すコードを貼り付けて、それを選択して渡すことはできますか? – fayyazkl

0

多分timeout引数に関連するものです。

timeoutの引数はselectとなりますか?

タイムアウト引数をより大きなものに変更し、レイテンシを観察してください。時にはタイムアウトが小さすぎるとシステムコールが実際にスループットを低下させることがあります。レイテンシが少し大きければ、より良い結果が得られるかもしれません。実現可能です。

タイムアウトまたはコードバグの疑いがあります。

+0

30ミリ秒 – AnkurVj

+0

'select'が返ってから' select'と 'read'を呼び出してコードを貼り付けることができますか? –

+0

@AnkurVjなぜですか?それは驚くほど短いタイムアウトです。たぶんあなたはプロセッサを飢えているかもしれません。 select()のタイムアウトは通常、少なくとも数秒です。 30ミリ秒ごとに何をしなければならないのですか? – EJP

1

私はこのコード行の不審次のようになります。

ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); 

あなたはリリースビルドを実行している場合は、その後、ASSERTはほとんど可能性が何も定義されているため、コールが実際に行われないであろう。 setsockopt呼び出しは、ASSERTステートメント内にあるべきではありません。代わりに、(変数内の)戻り値は、assertステートメントで検証される必要があります。 Asserts with side effectsは一般的に悪いことです。したがって、これが問題ではない場合でも、おそらく変更する必要があります。

+0

ああ私のコードで定義されているASSERTの部分は実際には表示されていません。それはうまく動作します。 – AnkurVj

+1

このコードはまだ反則的です。副作用を伴うコードを 'ASSERT'という名前のマクロの引数として置くことは、非デバッグビルドでは動作しないと思う可能性のある読者にとっては非常に混乱します。 –

+0

@AnkurVj:それが問題ないと思っても、アサートからコールを削除しようとするかもしれません。私はあなたの能力を疑っているわけではありませんが、初めて誰かがASSERTマクロを正しく書くことは稀です。 –

0

あなたはethtoolで無効にカーネル拡張GROGSOTSOTCP_CORK(CORK'edモード)を使用してみてください可能性があります

TCP_CORKフラグを立てセッション内で送信
  • データは、部分的に送信されないことを保証しますセグメント

  • 無効generic-segmentation-offload,およびtcp-segmentation-offloadは、カーネルが追加の収集に人為的な遅延を導入しないようにします私はこの問題は、ハードウェア関連であることを感じを持って/ユーザ空間から

関連する問題