2017-08-04 11 views
0

私のアプリケーションのサーバー部分で作業していますが、私は解決できないような問題に遭遇しました。次のようにConnectionManagerクラスの一部であるサーバの初期化関数は、である。ソケットプログラミング:listen()でエラーが発生しました

int ConnectionManager::init_server() { 

    // Log 
    OutputDebugString(L"> Initializing server...\n"); 

    // Initialize winsock 
    WSAData wsa; 
    int code = WSAStartup(MAKEWORD(2, 2), &wsa); 
    if (code != 0) { 
     // Error initializing winsock 
     OutputDebugString(L"> Log: WSAStartup()\n"); 
     output_error(code); 
     return -1; 
    } 

    // Get server information 
    struct addrinfo hints, *serverinfo, *ptr; 
    SOCKET sockfd = INVALID_SOCKET; 
    memset(&hints, 0, sizeof(struct addrinfo)); 
    hints.ai_protocol = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE; 
    hints.ai_family = AF_UNSPEC; 

    if (getaddrinfo(NULL, PORT, &hints, &serverinfo) != 0) { 
     // Error when getting server address information 
     OutputDebugString(L"> Log: getaddrinfo()\n"); 
     output_error(WSAGetLastError()); // Call Cleanup? 
     return -1; 
    } 

    for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) { 
     // Create socket 
     if ((sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == INVALID_SOCKET) { 
      // Error when creating a socket 
      OutputDebugString(L"> Log: socket()\n"); 
      output_error(WSAGetLastError()); // Call Cleanup? 
      continue; 
     } 

     // Set options 
     const char enable = 1; 
     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) == SOCKET_ERROR) { 
      // Error when setting options 
      OutputDebugString(L"> log: setsockopt()\n"); 
      output_error(WSAGetLastError()); // call cleanup? 
      if (closesocket(sockfd) != 0) { 
       output_error(WSAGetLastError()); 
      } 
      return -1; 
     } 

     // Bind socket 
     if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) { 
      // Error on binding 
      OutputDebugString(L"> Log: bind()\n"); 
      output_error(WSAGetLastError()); // Call Cleanup? 
      if (closesocket(sockfd) != 0) { 
       output_error(WSAGetLastError()); 
      } 
      continue; 
     }  

     break; 
    } 
    freeaddrinfo(serverinfo); 
    if (ptr == NULL) { 
     OutputDebugString(L"Error: Failed to launch server.\n"); 
     return -1; 
    } 
    // Listen 
    if (listen(sockfd, BACKLOG) == SOCKET_ERROR) { 
     OutputDebugString(L"> Log: listen()\n"); 
     output_error(WSAGetLastError()); // Call Cleanup?; 
     return -1; 
    } 
    // Accept 
    struct sockaddr_storage clientinfo; 
    int size = sizeof(struct sockaddr_storage); 
    m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size); 
    if (m_exchfd = INVALID_SOCKET) { 
     // Error when accepting 
     OutputDebugString(L"> Log: accept()\n"); 
     output_error(WSAGetLastError()); // Call Cleanup? 
     if (closesocket(sockfd) != 0) { 
      output_error(WSAGetLastError()); 
     } 
     return -1; 
    } 
    m_isConnected = true; 
    return 0; 
} 

output_error機能は、単にメッセージが​​関数を使用してエラーに対応する印刷します。しかし、私は次のような出力が得られます。

> Log: listen() 
> ERROR: The attempted operation is not supported for the type of object referenced. 

したがって、誤差が混乱してlisten()への呼び出しによって発生しなければなりません。誰が問題の原因を教えてくれますか?私はそれが何かを修正するのは簡単だろうと思うが、私はそれを見ていないようだ。

答えて

1

getaddrinfo()を呼び出すとき、問題の根本は、hints構造を間違って入力することです。

あなたはai_protocolフィールドにSOCK_STREAMを割り当てる、そして0 SOCK_STREAMai_socktypeフィールドセットを残すは、一般SOCK_DGRAMソケットと共に使用されるIPPROTO_ICMPと同じ値であり、これは、1と定義されます。したがって、getaddrinfo()は、フィールドがSOCK_DGRAMに設定されたaddrinfoエントリを返す可能性があります。あなたは、データグラムソケット、あなたが見ているので、WSAEOPNOTSUPPエラーにlisten()を使用することはできません。

エラーが発生していない場合は、listenリターンゼロ。それ以外の場合はSOCKET_ERRORの値が返され、WSAGetLastErrorを呼び出すと特定のエラーコードを取得できます。

...

WSAEOPNOTSUPP
参照ソケットがlisten操作をサポートタイプではありません。

代わりhints.ai_socktypeフィールドにSOCK_STREAMを割り当て、0又はIPPROTO_TCP(好ましくは後者)のいずれかにhints.ai_protocolフィールドを設定する必要があります。

また、のように、getaddrinfo()はエラーコードを返します。そのエラーコードを取得するのにWSAGetLastError()を使用しないでください。

それ以外にも、コードには他にもいくつかの問題があります。

  • SO_REUSEADDRchar(1バイト)は、BOOL(4バイト整数)を必要とします。あなたは単一のcharへのポインタを渡していますが、へのポインタを渡していることをsetsockopt()に伝えています。 setsockopt()はあなたが所有していないスタックメモリから値を読み取ろうとします。

  • あなたのループがclosesocket()を呼び出すと、あなたにもINVALID_SOCKETsockfdをリセットする必要があり、その後、ループの後、あなたは代わりにNULLをptrをチェックする、その条件を確認する必要があります。

  • ループの内側ではなく、listen()をループ内に呼び出す必要があります。 bind()ソケットは、割り当てられたリスニングポートを開くことができるとは限りません。実際にリスニングポートを正常に開くまでは、ループを維持する必要があります。ループの後に余分なログメッセージを追加して、どのローカルIP /ポートペアが実際にリッスンしているかを知ることもできます。その結果、クライアントはconnect()にできるクライアントを知ることができます。

  • WSAGetLastError()を呼び出すときは、失敗したWinsock呼び出し後には常にと呼びます。何かを事前に呼び出すと、WSAGetLastError()は多くのAPIで使用されているGetLastError()のエイリアスに過ぎないので、エラーコードをリセットする危険性があります。

  • m_exchfdINVALID_SOCKETに等しい場合は、=代入演算子の代わりに、チェック==比較演算子を使用している、accept()を呼び出します。あなたがそれを修正した後でさえ、accept()が成功すると、sockfdが漏れているので、そのトラックを失い、closesocket()を呼び出してはいけません。接続するクライアントが1つしかない場合は、クライアントが受け入れられた後にリスニングソケットを閉じます。それ以外の場合は、受信したソケットをクラスに格納し、受け入れたクライアントソケットを閉じた後に閉じます。そのすべてで

は、より多くのこのような何かをしようと、言った:私はあなたのコメントによると、私のプログラムを変更しようとした

void OutputWinsockError(LPCWSTR funcName, int errCode) 
{ 
    std::wostringstream msg; 
    msg << L"> Log: " << funcName << L"()\n"; 
    OutputDebugStringW(msg.str().c_str()); 
    output_error(errCode); 
} 

void OutputWinsockError(LPCWSTR funcName) 
{ 
    OutputWinsockError(funcName, WSAGetLastError()); 
} 

int ConnectionManager::init_server() { 

    // Log 
    OutputDebugString(L"> Initializing server...\n"); 

    // Initialize winsock 
    WSAData wsa; 
    int err = WSAStartup(MAKEWORD(2, 2), &wsa); 
    if (err != 0) { 
     // Error initializing winsock 
     OutputWinsockError(L"WSAStartup", err); 
     return -1; 
    } 

    // Get server information 
    struct addrinfo hints, *serverinfo, *ptr; 
    SOCKET sockfd = INVALID_SOCKET; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_flags = AI_PASSIVE; 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 

    err = getaddrinfo(NULL, PORT, &hints, &serverinfo); 
    if (err != 0) { 
     // Error when getting server address information 
     OutputWinsockError(L"getaddrinfo", err); 
     // Call Cleanup? 
     return -1; 
    } 

    for (ptr = serverinfo; ptr != NULL; ptr = ptr->ai_next) { 
     // Create socket 
     sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 
     if (sockfd == INVALID_SOCKET) { 
      // Error when creating a socket 
      OutputWinsockError(L"socket"); 
      continue; 
     } 

     // Set options 
     const BOOL enable = TRUE; 
     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(BOOL)) == SOCKET_ERROR) { 
      // Error when setting options 
      OutputWinsockError(L"setsockopt"); 
      if (closesocket(sockfd) == SOCKET_ERROR) { 
       // Error when closing socket 
       OutputWinsockError(L"closesocket"); 
      } 
      sockfd = INVALID_SOCKET; 
      continue; 
     } 

     // Bind socket 
     if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == SOCKET_ERROR) { 
      // Error on binding 
      OutputWinsockError(L"bind"); 
      if (closesocket(sockfd) == SOCKET_ERROR) { 
       // Error when closing socket 
       OutputWinsockError(L"closesocket"); 
      } 
      sockfd = INVALID_SOCKET; 
      continue; 
     }  

     // Listen on port 
     if (listen(sockfd, BACKLOG) == SOCKET_ERROR) { 
      // Error on listening 
      OutputWinsockError(L"listen"); 
      if (closesocket(sockfd) == SOCKET_ERROR) { 
       // Error when closing socket 
       OutputWinsockError(L"closesocket"); 
      } 
      sockfd = INVALID_SOCKET; 
      continue; 
     } 

     break; 
    } 

    freeaddrinfo(serverinfo); 

    if (sockfd == INVALID_SOCKET) { 
     OutputDebugString(L"Error: Failed to launch server.\n"); 
     // Call Cleanup? 
     return -1; 
    } 

    // Accept 
    struct sockaddr_storage clientinfo; 
    int size = sizeof(clientinfo); 

    m_exchfd = accept(sockfd, (struct sockaddr *)&clientinfo, &size); 
    if (m_exchfd == INVALID_SOCKET) { 
     // Error when accepting 
     OutputWinsockError(L"accept"); 
     if (closesocket(sockfd) == SOCKET_ERROR) { 
      OutputWinsockError(L"closesocket"); 
     } 
     // Call Cleanup? 
     return -1; 
    } 

    m_isConnected = true; 

    // is not storing sockfd, close it 

    // m_listenfd = sockfd; 
    if (closesocket(sockfd) == SOCKET_ERROR) { 
     OutputWinsockError(L"closesocket"); 
    } 

    return 0; 
} 
+0

、おかげで彼らのために多くのことを、私はまだ同じ取得しますエラー、両方のアドレスが見つかりました。また、setsockopt()関数でBOOLを使う理由は何ですか?それのプロトタイプによれば、代わりにcharを使用すべきですか? –

+1

'listen()'が失敗したときの 'ptr-> ai_family'、' ptr-> ai_socktype'、 'ptr-> ai_protocol'の実際の値は何ですか? 'AF_INET/6'、' SOCK_STREAM'、 'IPPROTO_TCP'でなければ何かが間違っています。ああ、待って、私はちょうどあなたのコードで別のエラーを見た。私は自分の答えを編集しました。 –

+1

['setsockopt()'のドキュメントを読んでください(https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476.aspx)。 'SO_REUSEADDR'は' BOOL'を待ちます。 'setsockopt()'のシグネチャを 'char *'ポインタを使ってだましてはなりません(論理的には 'void *'を使うべきです)。それは歴史的な時からのキャリーオーバーです。実際にはどんな種類のポインタも取ることができます。型キャストする必要があります。他のいくつかのソケットAPI関数と同様に、とりわけ 'sockaddr *'ポインタを取ると宣言されていますが、 'sockaddr_XX *'ポインタを実際に取ることができます。 –

関連する問題