使用C ++将流量转发到其他设备

时间:2017-02-03 22:57:42

标签: c++ sockets

我正在试用飞利浦Hue的一个小项目并同时学习c ++。我想要做的是在客户端和桥接器之间执行代理。就任何客户而言,应用程序作为桥梁运行,接收请求并将其传递给桥接器。

我有一个奇怪的问题,如果应用程序正常运行,我只得到一个HTTP 200 OK响应,没有别的,如果我单步执行代码,我得到完整的响应。

下面是我的代码,没有线程,没有类,它们都在main方法中完成。

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中正常运行应用程序时,我得到以下输出:

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

如果我设置断点然后单步执行代码,我会得到以下结果:

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

请注意,当我没有执行断点时,我只收到17个字节的HTTP 200 OK但是当我断点并逐步执行代码时,我得到超过600个字节并接收我期望的所有内容。

我看不出问题是什么,我已经把睡眠放在期待这会“修复”这个问题,但即使是睡眠没有任何区别。

1 个答案:

答案 0 :(得分:2)

send()/ recv()是用于使用套接字的低级调用。要正确处理HTTP(这是一个非常复杂的协议),您应该了解HTTP规范,请参阅即将过时的RFC2616

最好的办法是将receiveDataOnSocket()的do-while循环中的条件设置为while(bytesReceived&gt; 0)。只有在发送响应后服务器关闭连接时,它才会起作用。

如果使用持久连接,那么您可以做的下一个最好的事情是使用非阻塞套接字(至少用于接受来自客户端和服务器的数据),并且当数据到达时,将它们转发到另一端。它可能有用,但也可能失败。

接下来就是实现HTTP,这对于C ++教程来说太复杂了。