質問: C言語でミドルウェア(データストア)にクエリを投げて結果を得るプログラムを自作しています。 最初は fputs(3) や fgets(3) で行ごとに気楽にやっていたのですが、テキストじゃないバイナリデータでのやり取りが必要なクエリを扱う際にバグりました。 そこで send(2) や recv(2) に切り替えたのですが、今度は一回の recv(2) で得られたクエリ結果が中途半端な箇所で欠けていて、再度 recv(2) で取りに行かないといけなくなりました。 しかしもう全部クエリ結果を取り終えた状態で再度 recv(2) を呼ぶと、設定したタイムアウトまでブロックが発生して使い物にならなくなりました。 recv(2) で得られるクエリ結果の欠け方が毎回違って困りました。単純な while ループでやると上記ブロッキング問題に悩まされました。 最終的に 1 byte ずつ見て行きながら期待するクエリ結果が全部取得できたかどうか判断するしかありませんでした。 この recv(2) を用いたサーバーからの受信プログラムにおいて、1回のリクエストでレスポンスを全部受信できたかどうかを判断する良い方法はあるのでしょうか。今回の例でいうとミドルウェアのプロトコルのパース結果で判断せざるを得ないのでしょうか。それとも取得待ちのデータがあるかどうかを判断できる便利な関数や方法がありますか? そもそもノンブロッキングで実装した方がシンプルに書けるのでしょうか。 自分はC言語の鍛錬がまだ浅くモヤモヤしています。見当違いな質問でしたら恐縮ですがご教示いただけますと幸いです。 作成したプログラムは redis-cluster-data-transfer で、ミドルウェアのプロトコルは RESP2 です。
回答: ブロックされて困るのであれば、 ``` int val = 1; ioctl(sock, FIONBIO, &val); ``` でノンブロッキングソケットとすることです。この場合、`recv`は受信済みバッファに受信済みのデータのみが読み出せることになり、待つことはありません。もし受信バッファが空の場合、何も読み出せないため`recv`はエラーを返すことになるため、適切に対処する必要があります。 それと当然ですが、`recv`は待たなくなるためタイムアウトの設定は意味を成さなくなり、ご自身で時間管理する必要があります。