3

ノンブロッキング TCP ソケットを使用しようとしています。問題は、彼らがまだブロックしていることです。コードは以下です -

サーバーコード -

struct sockaddr name;
char buf[80];

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;   //sock is this socket, new_sd is connection socket

    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    //make socket
    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nBind error %m", errno);
        exit(1);
    }

    //unlink and bind
    unlink("127.0.0.1");
    if(bind (sock, &name, adrlen) < 0)
        printf("\nBind error %m", errno);

    //listen
    if(listen(sock, 5) < 0)
        printf("\nListen error %m", errno);

    //accept
    new_sd = accept(sock, &name, (socklen_t*)&adrlen);
    if( new_sd < 0) {
        cout<<"\nserver accept failure "<<errno;
        exit(1);
    }

    //set nonblock
    set_nonblock(new_sd);

    char* in = new char[80];
    std::string out = "Got it";
    int numSent;
    int numRead;

    while( !(in[0] == 'q' && in[1] == 'u' && in[2] == 'i' && in[3] == 't') ) {

        //clear in buffer
        for(int i=0;i<80;i++)
            in[i] = ' ';

        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str()) > 0) {
            numSent = send(new_sd, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

        numRead = recv(new_sd, in, 80, 0);
        if(numRead > 0)
            cout<<"\nData read from client - "<<in;

     }   //end while

     cout<<"\nExiting normally\n";
     return 0;
}

クライアントコード -

struct sockaddr name;

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nserver socket failure %m", errno);
        exit(1);
    }

    //stuff for server socket
    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    if(connect(sock, &name, adrlen) < 0) {
        printf("\nclient connection failure %m", errno);
        exit(1);
    }

    cout<<"\nSuccessful connection\n";

    //set nonblock
    set_nonblock(sock);

    std::string out;
    char* in = new char[80];
    int numRead;
    int numSent;


    while(out.compare("quit")) {

        //clear in
        for(int i=0;i<80;i++)
            in[i] = '\0';


        numRead = recv(sock, in, 80, 0);

        if(numRead > 0)
            cout<<"\nData read from server - "<<in;


        cout<<"\n";
        out.clear();
        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str())) {
            numSent = send(sock, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

    }   //end while


    cout<<"\nExiting normally\n";
    return 0;
}

私がそれを実行するときはいつでも、サーバーはクライアントが送信したものを読み取って出力する前に、私が何かを送信するのを待っています。サーバーまたはクライアントのいずれかが、入力するとすぐにメッセージを送信できるようにし、その時点でもう一方がメッセージを読み取って出力できるようにします。ノンブロッキングソケットが答えだと思いましたが、何か間違ったことをしているだけなのでしょうか?

また、sockaddr のデータとして、127.0.0.1 アドレスの代わりにファイルを使用していました。それが適切に使用されるべき方法ではない場合は、遠慮なくそう言ってください(以前はファイルで機能していたので、そのままにしておきました)。

どんな助けでも大歓迎です。

4

4 に答える 4

5

同時に多くの接続を処理したい TCP サーバーの一般的なアプローチ:

  • listen ソケットをノンブロッキングにする
  • イベントセットに追加select(2)または読み取りpoll(2)
  • 入るselect(2)/poll(2)ループする
  • ウェイクアップ時に、それがリッスン ソケットかどうかを確認してから、
    • accept(2)
    • 失敗を確認します (クライアントは接続の試行を既に中止している可能性があります)。
    • 新しく作成されたクライアント ソケットをノンブロッキングにし、ポーリング イベント セットに追加します。
  • そうでなければ、それがクライアント ソケットの 1 つである場合
    • 入力を消費し、処理する
    • EAGAINエラー コードに注意してください。実際にはエラーではありませんが、入力がないことを示しています。
    • close(2)ゼロバイトを読み取った場合 - クライアントは接続、クライアントソケットを閉じ、イベントセットから削除します
  • re-init イベント セット (これを省略すると、 でよくあるエラーになりますselect(2))
  • ループを繰り返す

ソケットが 1 つしかないため、クライアント側は少し単純です。ただし、多くの接続を処理する Web ブラウザーなどの高度なアプリケーションは、多くの場合、ノンブロッキングを行いconnect(2)ます。

于 2011-07-14T21:35:56.090 に答える
3

非ブロックをもっと早く設定する必要があると思います(つまり、ソケットを取得してから非ブロックに設定します)

また、それを設定するためのfcntlが実際に機能することを確認してください

于 2011-07-14T20:37:04.870 に答える
3

私がそれを実行するときはいつでも、サーバーはクライアントが送信したものを読み取って出力する前に、私が何かを送信するのを待っています。

まあ、それはあなたがそれを書いた方法です。stdin からの IO をブロックしてから、送受信を行います。

cin>>out;
cin.get();

また、プロセス間通信のためにファイルシステムに特別なファイルを作成するローカルソケット(AF_UNIX)を使用しています-これはIPとは異なるメカニズムであり、質問で示したようにTCPではありません。ファイルに 127.0.0.1 という名前を付けることができると思いますが、それは実際には意味がなく、混乱を招くことになります。これは IP ループバック アドレスであるためです。IP には AF_INET を使用する必要があります。

Unix ネットワーキングに関する優れたスターター ガイドについては、http://beej.us/guide/bgnet/をお勧めします。

受信したメッセージの表示を cin ステートメントから独立させたい場合は、別のプロセスから fork() してネットワーク IO を処理するか、別のスレッドを使用します。

select() に興味があるかもしれません。私の意見では、ノンブロッキング ソケットは通常はハックであり、select() または poll() を適切に使用することは、一般的にはるかに優れた設計であり、柔軟性が高い (そして移植性が高い) ものです。試す

男 select_tut

詳細については。

于 2011-07-14T20:52:54.173 に答える
0

ノンブロッキング I/O が必要な場合は、select を使用します。クライアントソケットとともに、リッスンしているソケットの1つとして標準入力で設定できます(標準入力であるファイル記述子1をfd_setに追加するだけです)。

http://beej.us/guide/bgnet/output/html/multipage/advanced.html

beej が select について述べていることを読むことをお勧めします。少し難しそうに見えますが、少し時間をかけて頭を包むと、本当に便利で簡単に使用できます。

于 2011-07-14T21:11:09.613 に答える