2017-02-03 5 views
-2

私はPhilips Hueの小さなプロジェクトを試して、同時にC++を学んでいます。私がしようとしているのは、クライアントとブリッジの間でプロキシを動作させることです。このアプリケーションは、クライアントが関係する限りブリッジとして動作し、要求を受け取り、これをブリッジに渡します。他のデバイスにトラフィックを転送するC++

アプリが正常に実行されると、HTTP 200 OKレスポンスしか得られません。コードをステップ実行すれば、完全な応答が得られます。

以下は私のコードです、スレッドはありません、クラスはありません、そのすべてがメインメソッドで行われています。

WindowsSocket socketManager(&bitsLibrary); 


    if (!socketManager.createSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 80, 1024)) 
    { 
     cout << "Failed to create socket" << endl; 
    } 

    socketManager.bindAndStartListening(); 

    WindowsSocket bridgeSocketManager(&bitsLibrary); 



    while (true) 
    { 

     sockaddr_in clientAddr; 
     memset(&clientAddr, 0, sizeof(sockaddr_in)); 
     SOCKET clientSocket = socketManager.acceptClientAndReturnSocket(&clientAddr); 
     this_thread::sleep_for(chrono::milliseconds(500)); 
     string received = socketManager.receiveDataOnSocket(&clientSocket); 
     bitsLibrary.writeToLog(received); 

     struct sockaddr_in server; 
     server.sin_family = AF_INET; 
     //server.sin_addr.s_addr = inet_addr("139.162.223.149"); 
     server.sin_addr.s_addr = inet_addr("192.168.1.67"); 
     server.sin_port = htons(80); 
     bridgeSocketManager.createSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
     this_thread::sleep_for(chrono::milliseconds(500)); 
     int result = connect(*bridgeSocketManager.returnSocket(), (struct sockaddr *)&server, sizeof(server)); 
     if (result < 0) 
     { 
      cout << "Connect Failed: " << WSAGetLastError() << endl; 
      return EXIT_FAILURE; 
     } 
     this_thread::sleep_for(chrono::milliseconds(500)); 

     SOCKET * bridgeSocket = bridgeSocketManager.returnSocket(); 
     this_thread::sleep_for(chrono::milliseconds(500)); 
     socketManager.sendToSocket(bridgeSocket, received); 
     string reply = socketManager.receiveDataOnSocket(bridgeSocket); 

     //boost::replace_all(reply, "Host: 192.168.1.70", "Host: 192.168.1.67"); 
     bitsLibrary.writeToLog(reply); 
     this_thread::sleep_for(chrono::milliseconds(1000)); 
     int sent = socketManager.sendToSocket(&clientSocket, reply); 
     this_thread::sleep_for(chrono::milliseconds(500)); 
     bridgeSocketManager.closeSocket(); 
     this_thread::sleep_for(chrono::milliseconds(500)); 
     socketManager.closeSocket(&clientSocket); 
     /*while (true) 
     { 
      SOCKET clientSocket = socketManager.acceptClientAndReturnSocket(&clientAddr); 
      SocketProcessor socketProcessor; 
      socketProcessor.startThread(socketManager, clientSocket); 
     }*/ 
    } 
    socketManager.closeSocket(); 

私のソケットは、次のような方法がある受け取る:

std::string WindowsSocket::receiveDataOnSocket(SOCKET *socket) 
{ 
    if (*socket != -1) 
    { 
     string receivedData = ""; 
     char *temp = NULL; 
     int bytesReceived = 0; 
     do 
     { 
      bytesReceived = recv(*socket, this->buffer, this->bufferLength, 0); 
      if (bytesReceived == SOCKET_ERROR) 
      { 
       string socketError = this->getErrorStringFromErrorCode(WSAGetLastError()).c_str(); 

       stringstream logstream; 
       logstream << "Failed to receive data on socket.The socket will now be closed and cleanup performed. Error: " << socketError; 

       this->bitsLibrary->writeToLog(logstream.str(), "WindowsSocket", "receiveDataOnSocket"); 
       closesocket(*socket); 
       WSACleanup(); 
       throw SocketException(socketError.c_str()); 
       return ""; 
      } 

      //If we got here, then we should be able to get some data 
      temp = new char[bytesReceived + 1]; 
      //memset(&temp, 0, bytesReceived + 1); 
      strncpy(temp, this->buffer, bytesReceived); 
      temp[bytesReceived] = '\0'; //Add a null terminator to the end of the string 
      receivedData.append(temp); 
      temp = NULL; 

      //Now clear the buffer ready for more data 
      memset(this->buffer, 0, this->bufferLength); 
      cout << "Bytes Received: " << bytesReceived << " BUffer Length: " << this->bufferLength << endl; 
     } while (bytesReceived == this->bufferLength && bytesReceived >= 0); //Keep going until the received bytes is less than the buffer length 

     return receivedData; 
    } 
    else 
    { 
     stringstream logstream; 
     logstream << "Can't receive on socket as already be closed"; 
     throw SocketException(logstream.str().c_str()); 
    } 
} 

次のようにsendメソッドが見えます:

int WindowsSocket::sendToSocket(SOCKET *clientSocket, string dataToSend) 
{ 
    //dataToSend.append("\r\n"); 
    int sentBytes = send(*clientSocket, dataToSend.c_str(), dataToSend.length(), 0); 
    if (sentBytes == SOCKET_ERROR) 
    { 
     throw SocketException(this->getErrorStringFromErrorCode(WSAGetLastError()).c_str()); 
    } 
    return sentBytes; 
} 

ノーブレークポイントでのVisual Studioで正常にアプリを実行すると、I次の出力を取得します。

03/02/2017 22:08:16: WindowsSocket/bindAndStartListening: Socket has binded and is now listening 
Bytes Received: 413 BUffer Length: 1024 
Receiving data 
GET /api/nouser/config HTTP/1.1 
Host: 192.168.1.70 
Connection: keep-alive 
Cache-Control: max-age=0 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
Accept-Encoding: gzip, deflate, sdch 
Accept-Language: en-GB,en;q=0.8 

03/02/2017 22:08:22: BaseSocket/createSocket:  Creating buffer of length 1024 
Bytes Received: 17 BUffer Length: 1024 
03/02/2017 22:08:23: HTTP/1.1 200 OK 
Sent: 17 

私はブレークポイントを設定し、その後、私は、次の取得コードをステップ実行した場合:私は、ブレークポイントをしないとき、私はHTTPの唯一の17バイトは、200 OK受けるが、私はブレークポイントを設定してステップ際

03/02/2017 22:09:03: WindowsSocket/bindAndStartListening: Socket has binded and is now listening 
Bytes Received: 413 BUffer Length: 1024 
GET /api/nouser/config HTTP/1.1 
Host: 192.168.1.70 
Connection: keep-alive 
Cache-Control: max-age=0 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
Accept-Encoding: gzip, deflate, sdch 
Accept-Language: en-GB,en;q=0.8 

03/02/2017 22:09:09: BaseSocket/createSocket:  Creating buffer of length 1024 
Bytes Received: 630 BUffer Length: 1024 
03/02/2017 22:09:17: HTTP/1.1 200 OK 
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 
Pragma: no-cache 
Expires: Mon, 1 Aug 2011 09:00:00 GMT 
Connection: close 
Access-Control-Max-Age: 3600 
Access-Control-Allow-Origin: * 
Access-Control-Allow-Credentials: true 
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, HEAD 
Access-Control-Allow-Headers: Content-Type 
Content-type: application/json 

{"name":"Philips hue","datastoreversion":"59","swversion":"01036659","apiversion":"1.16.0","mac":"00:17:88:1a:1f:43","bridgeid":"001788FFFE1A1F43","factorynew":false,"replacesbridgeid":null,"modelid":"BSB001"} 
Sent: 630 

お知らせコードを介して私は600バイト以上を取得し、私が期待していたすべてを受け取ります。

私は問題が何かを見ることができません、私はこれが問題を "修正"することを期待して眠りを入れましたが、睡眠でさえ違いはありません。

ご協力いただきありがとうございます。

+0

あなたの条件(bytesReceived == this-> bufferLength)は無意味です –

+0

[HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol)の詳細は、想像以上に複雑です。 HTTPサーバーまたはHTTPクライアント・ライブラリーの使用を検討してください。 [libcurl](https://curl.haxx.se/libcurl/)(HTTPクライアント)と[libonion](http://coralbits.com/libonion/)(HTTPサーバー)を調べます。 [POCO](https://pocoproject.org/)、[Boost](http://boost.org/)、[Qt](http://qt.io/)のフレームワークも参照してください。 –

答えて

2

send()/ recv()は、ソケットで作業する低レベルの呼び出しです。非常に複雑なプロトコルであるHTTPを適切に処理するには、HTTP仕様を理解しておく必要があります。詳細についてはRFC2616を参照してください。

receiveDataOnSocket()のdo-whileループで条件をwhile(bytesReceived> 0)に設定することをお勧めします。応答を送信した後にサーバーが接続を終了した場合にのみ機能します。

永続的な接続が使用されている場合、次にできることは、非ブロッキングソケットを使用することです(少なくともクライアントとサーバーの両方のデータを受け入れるためです)。データが到着すると、それらを他の側に転送します。それはうまくいくかもしれませんが、失敗することもあります。

次は実際にHTTPを実装していますが、これはC++チュートリアルでは複雑すぎます。

+0

バッファが常に満たされるようなデータがあれば、私が期待したようにbytesReceived == bufferLengthを置いていました。もし私がちょうどbytesReceived> 0と言うなら、それはrecvメソッドに戻り、何もないときにはもっとデータを待っているのでハングアップします。 – Boardy

+0

HTTPを見ていただきありがとうございますソケットから読み取って別のソケットにまっすぐチャッキングするより複雑です。 – Boardy

+0

私が見たことから、私がやっていることは、ちょうど1つのソケットからデータを受け取り、別のものにチャッキングするだけで動作するはずです。これはコードを踏んでいくことによっても証明されています。転送されたデバイスからの完全な応答を取得します。私はrecv関数の直前に10msの睡眠を入れたテストを行いましたが、今は完全にうまく動作します。 – Boardy

関連する問題