2016-11-20 4 views
1

シンプルなソケットサーバーを作成しようとしていますが、この例ではエコーサーバーです。なぜ「選択」しても「受け入れる」必要があると思われますか?

これは私のメインプログラムに続いてsocket_server.c(それが立ち往生場所についてのコメントを注意してください)

int fdmax, i, socket_descriptor; 
fd_set master, read_fds; 
struct timeval tv = {.tv_sec = 0, .tv_usec = 1000}; 

void socket_server_init(void) 
{ 
    FD_ZERO(&master); 
    FD_ZERO(&read_fds); 
} 

socket_server_socket socket_server_start(char *socket_path) 
{ 
    struct sockaddr_un local; 
    int len; 
    int socket_descriptor = socket(AF_UNIX, SOCK_STREAM, 0); 

    if (socket_descriptor == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_SOCKET_FAILURE, .descriptor = -1}; 

    local.sun_family = AF_UNIX; 
    strcpy(local.sun_path, socket_path); 
    unlink(local.sun_path); 
    len = strlen(local.sun_path) + sizeof(local.sun_family); 

    if (bind(socket_descriptor, (struct sockaddr *)&local, len) == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_BIND_FAILURE, .descriptor = -1}; 

    if (listen(socket_descriptor, 5) == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_LISTEN_FAILURE, .descriptor = -1}; 

    FD_SET(socket_descriptor, &master); 
    fdmax = socket_descriptor; 
    return (socket_server_socket) {.status = 0, .descriptor = socket_descriptor}; 
} 

socket_server_socket socket_server_wait_for_connection(socket_server_socket server) 
{ 
    read_fds = master; 
    select(fdmax+1, &read_fds, NULL, NULL, &tv); 

    for (i = 0; i <= fdmax; i++) 
    { 
     if (FD_ISSET(i, &read_fds)) 
     { 
      if (i == server.descriptor) 
      { 
       // It's getting stuck here. 
       socket_descriptor = accept(server.descriptor, (struct sockaddr *) NULL, NULL); 

       if (socket_descriptor > fdmax) 
        fdmax = socket_descriptor; 

       FD_SET(socket_descriptor, &master); 
       return (socket_server_socket) {.status = -1, .descriptor = socket_descriptor}; 
      } else { 
       return (socket_server_socket) {.status = 0, .descriptor = i}; 
      } 
     } 
    } 

    return (socket_server_socket) {.status = -1, .descriptor = -1}; 
} 

int socket_server_update(socket_server_socket client) 
{ 
    char buffer[256]; 
    int n = recv(client.descriptor, buffer, 256, 0); 
    if (n < 0) 
     return SOCKET_SERVER_UPDATE_RECV_FAILURE; 

    if (send(client.descriptor, buffer, n, 0) < 0) 
     return SOCKET_SERVER_UPDATE_SEND_FAILURE; 

    close(client.descriptor); 
    return 0; 
} 

です:

:私は、私が観察し、プログラムを実行すると

socket_server_init(); 
    socket_server_socket server = socket_server_start(SOCKET_PATH); 
    while (1) { 
     printf("wait for conn\n"); 
     socket_server_socket client = socket_server_wait_for_connection(server); 

     if (client.status == 0) 
     { 
      socket_server_update(client); 
     } 

     sleep(1); 
     printf("%d: Log !\n", (int)time(NULL)); 
    } 

  1. "ログ!"コンソールから出力されます
  2. ソケットを介してサーバーに接続します
  3. "ログ!"コンソール
  4. に私は
  5. クライアントがデータを
  6. エコーバック見ているクライアントからのいくつかのデータを送信し、「ログ!」もはやクライアントからのそれ以降のデータはバック

エコーされませんコンソール

  • に見られることは、私はループが続けていくとして期待されていない「ログ!」私のプログラムがaccept呼び出しで停止しているように見えますが、2回目です。

    私が理解しているように、selectは、受け入れるかrecvする必要がある場合にのみ、read_fdsに記述子を追加することになっています。そうacceptacceptなるのを待っているものは何もありませんread_fds

  • へのサーバーの記述を追加することが再び
  • selectを受け入れますread_fds
  • に、サーバーの記述を追加し

    1. select:だから何がある発生するようですハングします

    私が確認したのは、accept呼び出しに達すると、記述子は同じです。だから私は非常に混乱しています。

    私は間違っていますか?私は同じ接続のためにもう一度acceptを打つべきではないと確信しています。

  • 答えて

    1

    このタイムアウトが経過すると、select(2)は戻り値を気にせず、成功した呼び出し(「われわれは何かを読んでいます」)として扱いますが、read_fds hasn '更新されました。したがって、それは以前の値( "新しいクライアントがあります")を保持していますが、acceptに電話をかけてしまいました。

    ソリューションは非常に簡単になります:select(2)の戻り値をチェックします。

    int result = select(fdmax+1, &read_fds, NULL, NULL, &tv); 
    if (result < 0) { 
        // Handle error. 
    } else if (result == 0) { 
        // Handle timeout. 
    } 
    

    共通で、UNIXでselectとソケットの詳細については、私は非常にBeej's Guide to Network Programmingを読んでお勧めします。

    +0

    これは近いと思われます...私はあなたの提案に従うことができません。なぜなら、接続を待つ間にselectをブロックするためです。私はタイムアウトを使用していますので、私のプログラムの他の部分は引き続き作業できます。私は 'if(select(fdmax + 1、&read_fds、NULL、NULL、&tv)<0)return;'を試しましたが、 'select'が何らかの理由で-1を返すという状況に陥ります。 –

    +0

    あなたの 'fd_set'から無効なソケットを' FD_CLR'する必要があることに注意してください。 。単純なチャットのためにbeejの例に従ってみて、あなたのプログラムと比較してみてください。 selectが-1を返す理由を調べるには、最後のエラーを 'perror'で表示してみてください。 –

    +0

    @CameronBall selectが0を返した場合、タイムアウトが発生しています。それが<0を返す場合は、エラー*があり、エラーが何であるかを調べるには、errnoをチェックしたり、perrorを使用する必要があります。 –

    関連する問題