2016-04-15 15 views
-1

プロジェクトの場合、私はinetdのような "スーパーサーバ"を構築しています。設定ファイルから一連のポートとコマンドを読み込み、ポートごとにリスナーソケットをスピンアップさせることになっています。次に、select()を使用して、これらのソケットのうちの1つ以上がいつ読み込み可能かを判断する必要があります。 selectがソケットを見つけたら、accept()を使ってこのソケットに接続し、コマンドを実行する子プロセスをfork()する必要があります。残念なことに、selectは、 "nc -l localhost 12345"を呼び出して(config.txtファイルの '12345 echo "hello world"を使って)テストしようとすると、常にタイムアウトまたは失敗します。Select()コールが動作しないlocalhost

私が間違っている可能性のあるものを見つけることができますか?前もって感謝します!私はこれを理解しようと夢中になってきた!

#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <sys/select.h> 

#include <iostream> 
#include <sstream> 
#include <fstream> 
#include <map> 

using namespace std; 

map<int,string> parse_config_file() { 
    string line; 
    ifstream file; 
    stringstream ss; 
    int port; 
    string command; 
    map<int,string> port_to_command; 

    file.open("config.txt"); 

    while (getline(file,line)) { 
     ss = stringstream(line); 
     ss >> port; 
     getline(ss,command); 
     port_to_command[port] = command; 
    } 

    file.close(); 

    return port_to_command; 
} 

void handle_client(int socket, string command) { 
    dup2(socket, STDIN_FILENO); 
    dup2(socket, STDOUT_FILENO); 
    execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL); 
} 

int main(int argc, const char * argv[]) { 

    int rc; 
    int readyfd; 
    int peerfd; 
    int maxfd = 0; 
    int port; 

    pid_t child_pid; 
    fd_set readfds; 

    struct timeval tv; 
    tv.tv_sec = 10; 
    tv.tv_usec = 0; 

    struct sockaddr* server_address; 
    socklen_t server_address_length = sizeof(server_address); 
    struct sockaddr client_address; 
    socklen_t client_address_length = sizeof(client_address); 

    map<int,string> port_to_command = parse_config_file(); 
    map<int,string>::iterator pcitr; 
    map<int,int> socket_to_port; 
    map<int,int>::iterator spitr; 


    // Create, bind, and listen on the sockets: 
    for (pcitr = port_to_command.begin(); pcitr != port_to_command.end(); pcitr++) { 

     int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
     if (sockfd < 0) { 
      cerr << "ERROR opening socket"; 
      exit(EXIT_FAILURE); 
     } 

     port = pcitr->first; 
     struct sockaddr_in server_address_internet; 
     bzero((char *) &server_address_internet, sizeof(server_address_internet)); 
     server_address_internet.sin_family = AF_INET; 
     server_address_internet.sin_addr.s_addr = INADDR_ANY; 
     server_address_internet.sin_port = htons(port); 
     server_address = (struct sockaddr *)&server_address_internet; 

     bind(sockfd, server_address, server_address_length); 

     rc = listen(sockfd, 10); 
     if (rc < 0) { 
      cerr << "listen() failed"; 
      exit(EXIT_FAILURE); 
     } 

     socket_to_port[sockfd] = pcitr->first; 

     if (sockfd > maxfd) { 
      maxfd = sockfd; 
     } 
    } 

    // Server Loop 
    while (true) { 

     // Rebuild the FD set: 
     FD_ZERO(&readfds); 
     for (spitr = socket_to_port.begin(); spitr != socket_to_port.end(); spitr++) { 
      FD_SET(spitr->first, &readfds); 
     } 

     // Select 
     rc = select(maxfd + 1, &readfds, NULL, NULL, &tv); 
     if (rc == 0) { 
      // Timeout 
      continue; 
     } else if (rc < 0) { 
      cerr << "select failed" << endl; 
      exit(EXIT_FAILURE); 
     } 

     // Find the socket that is ready to be read: 
     readyfd = -1; 
     for (spitr = socket_to_port.begin(); spitr != socket_to_port.end(); spitr++) { 
      if (FD_ISSET(spitr->first, &readfds)) { 
       readyfd = spitr->first; 
       break; 
      } 
     } 

     // Accept 
     peerfd = accept(readyfd, &client_address, &client_address_length); 
     if (peerfd < 0) { 
      cerr << "accept failed" << endl; 
      exit(EXIT_FAILURE); 
     } 

     // Fork to handle request: 
     child_pid = fork(); 
     if (child_pid == 0) { 
      port = ((struct sockaddr_in*)&client_address)->sin_port; 
      handle_client(peerfd, port_to_command[port]); 
      close(peerfd); 
      exit(EXIT_SUCCESS); 
     } else { 
      close(peerfd); 
     } 
    } 

    return 0; 
} 
+0

これはコードレビューやデバッグサービスではありません。また、無関係な言語のタグをスパムしないでください。 – Olaf

答えて

1

さて、あなたが間違っていたことをいくつか見つけました。

  1. using namespace std; - 一部は明らかに間違っていること。

  2. parse_config_file()は、構成ファイルの構文を検証してチェックしません。タイプミスや誤った文字があると、operator>>が検出されずに失敗します。したがって、コマンドのポートは、ランダム、初期化されていない、または前のコマンドのポートのコピーのいずれかになります。

  3. そして、最後に、私たちはこれに来る:

    struct sockaddr* server_address; 
    socklen_t server_address_length = sizeof(server_address); 
    

ポップクイズ:はsizeof(sockaddr構造体*が)何ですか?まあ、それはポインタなので、ここでは4バイトか8バイトのいずれかになります。

bind(sockfd, server_address, server_address_length); 

私はstruct sockaddr_inはそれよりも大きいことをかなり確信しています。クイックチェックは、16バイト長であることを確認します。 16バイト構造体のサイズとして、4バイトまたは8バイトのいずれかを渡していました。

ここには2-ferがあります。サイズが間違っていて、bind()によって返されたエラーコードをチェックするのに失敗したので、システムコールが常に失敗していることに全く気づかなかった。

システムコールが常に成功するとは想定できません。 bind()socket()connect()、またはaccept()のいずれであろうと。すべてのシステムコールが失敗する可能性があります。すべてのシステムコールの戻り値を常にチェックしてください。システムコールの戻り値をチェックするのは退屈で退屈かもしれませんが、実行する必要があります。もしあなたがそうしたならば、あなたは最初のバグを間違ったものと考えていたでしょう。間違ったsizeof()

関連する問題