select を使用した同じソケット (TCP) への読み取りと書き込み
-
20-09-2019 - |
質問
私たちは、(私がそう思っていた) 非常に単純なネットワーク通信を行うクライアントとサーバーを作成しています。複数のクライアントがサーバーに接続すると、サーバーは他のすべてのクライアントにデータを送り返すことになります。
サーバーはブロッキング状態にあるだけです select
ループはトラフィックを待機し、トラフィックが到着すると、データを他のクライアントに送信します。これはうまくいくようです。
問題はクライアントです。読み取りに応答して、書き込みを実行する必要がある場合があります。
ただし、次のように使用すると次のことがわかります。
rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);
新しいデータを読み取るまでコードはブロックされます。ただし、場合によっては (別のスレッドから非同期的に) ネットワーク通信スレッドに新しいデータを書き込むことがあります。そこで、select を定期的に起動して、書き込むデータがあるかどうかをチェックできるようにしたいと考えています。
if (select(....) != -1)
{
if (FD_SET(sockfd, &master_list))
// handle data or disconnect
else
// look for data to write and write() / send() those.
}
私は選択をポーリングモード(またはとんでもなく短いタイムアウト)に設定してみました:
// master list contains the sockfd from the getaddrinfo/socket/connect seq
struct timeval t;
memset(&t, 0, sizeof t);
rv = select(fdmax + 1, &master_list, NULL, NULL, &t);
しかし、その後クライアントは受信データをまったく取得しないことがわかりました。
また、次のようにソケット fd をノンブロッキングに設定してみました。
fcntl(sockfd, F_SETFL, O_NONBLOCK);
しかし、それは問題を解決しません:
- 私のクライアントなら
select()
ありませんstruct timeval
, 、データの読み取りは機能しますが、書き込み可能なデータを探すためにブロックが解除されることはありません。 - 私のクライアントなら
select()
がありますtimeval
ポーリングを実行しても、読み取るべき受信データがあることを通知することはなく、(他のすべての関数呼び出しが成功しているにもかかわらず) ネットワーク接続が確立されていないと考えてアプリがフリーズします。
私が間違っている可能性があることについて何かヒントはありますか?同じソケット上で読み書きを行うことはできないのでしょうか (それが真実であるとは信じられません)。
(編集:正しい答えは、クライアントではなくサーバーでは覚えていたことですが、2 番目の fd_set を用意し、select() を呼び出すたびに master_list をコピーすることです。
// declare and FD_ZERO read_fds:
// put sockfd in master_list
while (1)
{
read_fds = master_list;
select(...);
if (FD_ISSET(read_fds))
....
else
// sleep or otherwise don't hog cpu resources
}
)
解決
すべてはあなたがif (FD_SET(sockfd, &master_list))
を行う行を除いて、正常に見えます。私は非常に類似したコードの構造を持っていると私はFD_ISSET
を使用しました。あなたは、リストが設定されている場合は、再度設定していないテストするはずです。それ以外は、私は他に何も表示されません。
編集。また、私はタイムアウトを以下ます:
timeval listening_timeout;
listening_timeout.tv_sec = timeout_in_seconds;
listening_timeout.tv_usec = 0;
おそらくあなたは0に設定すると問題があります(あなたがやっているように見えるよう?)
EDIT2。私は私が選択EXITED後に設定の読み取りをクリアしていないときに奇妙な問題に走ったと私は再びそれを入力する前に思い出しました。私のような何かをしなければならなかった。
FD_ZERO(&sockfd);
FD_SET(sockfd, &rd);
私はselect
に入る前に。私はなぜけれども覚えていないことができます。
他のヒント
私は、ネットワークスレッドを選択呼び出しでの記述子に追加され、メインスレッド間の読み取り/書き込みファイル記述子を作成し、共有についてのトリックを思い出しているようです。 このFDは、それが送信するために何かを持っているメインスレッドによってそれに書かれた1つのバイトを持っています。書き込みは、選択呼び出しからネットワークスレッドをウェイクアップし、ネットワークスレッドは、その後、選択にスリープ状態に戻って共有バッファからデータをつかみ、ネットワークに書き込みされます。
申し訳ありませんが、それは少し漠然としてコードの不足だ...と他の人が、さらにあなたをガイドする必要がありますので、私の記憶が...間違っている可能性があります。
場合私はあなたのコードに何か問題が表示されていないので、それが動作するはずです。あなたはそれが働いて得ることができない場合は、それを回避する一つの方法は、あなたの読書スレッドと書き込み用のものを用意したスレッドによって使用されるパイプを作成し、あなたのselect
セットにパイプの読取終了を追加することです。他のスレッドが書き込むためのデータを作成したときに、それはちょうどあなたの読書のスレッドがselect
から覚めます、パイプの上に何かを送信し、それがその後、書き込みを行うことができます。読み書きするデータが存在する頻度に応じて、これはまた、より効率的な可能性があります。
2つのスレッドはので、あなたのメインスレッドは、他の1は、受信データの選択待ちで眠っている間、クライアントに書き込むことができる必要があり、一度に同じソケットで作業することができるはずです。もちろんこれは、両方のスレッドがクライアントリストへのアクセス権を持っていることを前提としています。