2017-04-30 15 views
1

私はシンプルなサーバー/クライアントプログラムを書いています。クライアントは小さなコードでハードコードされたデータをサーバプログラムに送ります。ターミナル。クライアントでは、送信するデータがさらにある間にループ内でsend()を呼び出しています。サーバ上では、返されるバイト数が0より大きい間もread()と同じことをします。私は読んでいます。ソケット - データ転送後にソケットを開いたままにする

この例は、送信が完了した後にクライアントのソケットでclose()を呼び出すと完全に機能しますが、そうしないと、クライアントを閉じるまでサーバは実際にはread()ループを終了しません接続を解除します。サーバー側では、私が使用しています:すべてのデータを受信したとき

while((bytesRead = read(socket, buffer, BUFFER_SIZE)) > 0) 

は0 bytesReadないでしょうか?もしそうなら、なぜ私はソケットを閉じるまでこのループを終了しませんか?私の最終的なアプリケーションでは、リクエスト間でソケットを開いたままにしておくことは有益ですが、データを送信した直後にサンプルコードと情報をすべて見つけて閉じることができます。

私には何が欠けていますか?

+1

は、サーバー上で非ブロックのようにソケットをマークしましたそれを開いた? [最小限で完全で検証可能な例](https://stackoverflow.com/help/mcve)は... – fredrik

+0

電話を取る。あなたが知っている人に電話をする。 「こんにちは、どうですか?」と言ってください。何も言わないでください。ここでは、あなたが持っているすべてのデータを送信したばかりです。相手はいつ電話を切るべきですか? –

+0

@fredrik - 私は私の例では間違ったソケットをノンブロッキングに設定していました。ありがとうございました – Triforcer

答えて

3

ソケットのもう一方の端が世界中の他のネットワークシステムに接続されている場合、受信ソケットが「すべてのデータが受信されたとき」を知る唯一の方法は、ソケットの反対側が閉まっている。ソケットの反対側に「すべてのデータが受信された」と伝えます。

ソケットは、他のソケットエンドポイントに接続されていることを知っています。それでおしまい。物語の終わり。ソケットには、ソケット接続の反対側にあるプログラムの内部動作についての特別な知識はありません。それは知っているべきでもない。これは、ソケットそのものではなく、ソケットが開いているプログラムの責任です。

受信側で受信する必要があるデータをすべて受信したことを知っている受信側のプログラムが知識を持っている場合、手元の次のタスクに移動します。

プログラムのロジックに、すべてのデータが送信されているかどうかを何らかの形で判断する方法を組み込む必要があります。その正確な性質は、あなたが定義することになります。おそらく、ソケット上のすべてのデータを送信する前に、送信プログラムは、同じソケット上で、次に続くデータのバイト数を事前に送信します。次に、受信プログラムは、最初にバイト数を読み取り、次にデータそのものを読み取り、それがすべてを受信したことを知り、移動することができます。

これは単純なアプローチの1つです。正確な詳細はあなた次第です。また、タイムアウトを実装することもできます。タイマーを設定し、一定の時間内にデータが受信されない場合は、それ以上存在しないと仮定します。

+0

情報をありがとう、私はソケットの内部の動作をまったく理解していないことを認めなければなりません。それはすべてが綴られているとき論理的に意味をなす – Triforcer

0

recv呼び出しにフラグを設定して、ブロックを防ぐことができます。簡単にこれを検出する

一つの方法は、RECV呼び出しをラップすることです:

enum class read_result 
{ 
    // note: numerically in increasing order of severity 
    ok, 
    would_block, 
    end_of_file, 
    error, 
}; 

template<std::size_t BufferLength> 
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read) 
{ 
    auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT); 
    if (result > 0) 
    { 
     return read_result::ok; 
    } 
    else if (result == 0) 
    { 
     return read_result::end_of_file; 
    } 
    else { 

     auto err = errno; 

     if (err == EAGAIN or err == EWOULDBLOCK) { 
      return read_result::would_block; 
     } 
     else { 
      return read_result ::error; 
     } 
    } 
} 

つのユースケースは次のようになりますとき

#include <unistd.h> 
#include <sys/socket.h> 
#include <cstdlib> 
#include <cerrno> 
#include <iostream> 

enum class read_result 
{ 
    // note: numerically in increasing order of severity 
    ok, 
    would_block, 
    end_of_file, 
    error, 
}; 

template<std::size_t BufferLength> 
read_result read(int socket_fd, char (&buffer)[BufferLength], int& bytes_read) 
{ 
    auto result = recv(socket_fd, buffer, BufferLength, MSG_DONTWAIT); 
    if (result > 0) 
    { 
     return read_result::ok; 
    } 
    else if (result == 0) 
    { 
     return read_result::end_of_file; 
    } 
    else { 

     auto err = errno; 

     if (err == EAGAIN or err == EWOULDBLOCK) { 
      return read_result::would_block; 
     } 
     else { 
      return read_result ::error; 
     } 
    } 
} 

struct keep_reading 
{ 
    keep_reading& operator=(read_result result) 
    { 
     result_ = result; 
    } 

    const operator bool() const { 
     return result_ < read_result::end_of_file; 
    } 

    auto get_result() const -> read_result { return result_; } 

private: 
    read_result result_ = read_result::ok; 
}; 

int main() 
{ 
    int socket; // = open my socket and wait for it to be connected etc 

    char buffer [1024]; 
    int bytes_read = 0; 
    keep_reading should_keep_reading; 
    while(keep_reading = read(socket, buffer, bytes_read)) 
    { 
     if (should_keep_reading.get_result() != read_result::would_block) { 
      // read things here 
     } 
     else { 
      // idle processing here 
     } 
    } 

    std::cout << "reason for stopping: " << should_keep_reading.get_result() << std::endl; 
} 
関連する問題