Winsock2.h无法发送http请求

时间:2018-05-23 11:00:44

标签: c++ http networking winsock2

致力于其他程序员,

我正在尝试编写一个允许您输入所需内容的程序,程序会将您的输入发送到服务器。

目前,我的目标是将HTTP请求发送到网页。它很好。但是当while循环运行时立即通过cin.getline过程发送一些东西,而我没有输入任何东西。我觉得这很奇怪,但无论如何它似乎都有效。

每次我发送类似的东西:“GET / HTTP / 1.1 \ r \ n \ r \ n”它将返回正确的内容,但我输入的任何其他内容,如“OPTIONS”返回源代码+“应用程序被阻止“(我在学校这是有道理的)。

所以,我连接到热点屏蔽VPN并测试了应用程序,但是当我输入要发送的内容时,我感到非常恐怖。

我搜索了堆栈溢出和谷歌,但到目前为止我还没有找到任何东西;可能是因为我正在寻找问题的错误解决方案。 无论如何,如果你有时间,请通过代码扫描发送一些帮助。它可能只是一个VPN和学校问题,如果代码似乎对你有用,我可以在家里试试,所以请告诉我。

问题的具体概述: 当我在学校网络外使用它时,没有返回任何内容,并且while循环似乎没有执行。我可以连接,但程序似乎是在无休止的时间或其他东西。

    cout << "Connected to " << hostName << endl;



    while (true) {
        cout << ">";


        cin.getline(sendBuf, sizeof(sendBuf));
        string s(sendBuf);


        cout << s.c_str() << endl;


            send(connectSocket, s.c_str(), sizeof(s.c_str()), 0);

            int rec = recv(connectSocket, recvBuf, sizeof(recvBuf), 0);
            if (rec > 0) {
                cout << recvBuf << endl;
            }
            else if (rec <= 0) {
                cout << "nothing" << endl;
            }
        }

    system("pause");

}
system("pause");
}

1 个答案:

答案 0 :(得分:0)

  

我的目标是将HTTP请求发送到网页

您展示的代码不会尝试实现HTTP protocol的任何外观,甚至不会关闭。

首先,如果您仔细查看自己的示例,由于您使用HTTP 1.1,您将看到GET请求(BTW,缺少必需的Host标头)包含2个换行符,但cin.getline()(为什么不std::getline()?)一次只读取1行。因此,您在一行中阅读,发送它,并等待一个没有到达的响应,因为您还没有完成发送完整的请求。这可以解释为什么你的while循环挂起了。

如果您希望用户键入完整的HTTP请求,然后按原样发送,则必须先读取用户的ENTIRE请求,然后将其完全发送到服务器,然后才能尝试接收服务器的响应。这意味着您必须处理各个邮件头之间的换行符,处理将邮件头与邮件正文分开的终止换行符,并检测正文数据的结尾。

我建议不要依赖用户按原样键入完整的HTTP请求。我建议你提示用户输入相关部分并让用户输入普通文本,然后你的代码可以根据需要将该文本格式化为正确的HTTP请求。

当您阅读服务器的响应时,您不能盲目地读取任意数据块。您必须根据HTTP协议的规则处理您阅读的内容。这对于确定何时到达响应结束并需要停止读取尤其重要。如RFC 2616 Section 4.4 Message Length中所述,可以通过多种不同方式之一发出响应的结束信号。

您通常也会在TCP处理中犯一些常见的新手错误。 TCP是一种流传输,您没有考虑send()recv()可以发送/接收的字节数少于请求的字节数。或者recv()不返回以null结尾的数据。

话虽如此,尝试这样的事情:

void sendAll(SOCKET sckt, const void *buf, int buflen)
{
    // send all bytes until buflen has been sent,
    // or an error occurs...

    const char *pbuf = static_cast<const char*>(buf);

    while (buflen > 0)
    {
        int numSent = send(sckt, pbuf, buflen, 0);
        if (numSent < 0) {
            std::ostringstream errMsg;
            errMsg << "Error sending to socket: " << WSAGetLastError();
            throw std::runtime_error(errMsg.str());
        }
        pbuf += numSent;
        buflen -= numSent;
    }
}

int readSome(SOCKET sckt, void *buf, int buflen)
{
    // read as many bytes as possible until buflen has been received,
    // the socket is disconnected, or an error occurs...

    char *pbuf = static_cast<char*>(buf);
    int total = 0;

    while (buflen > 0)
    {
        int numRecvd = recv(sckt, pbuf, buflen, 0);
        if (numRecvd < 0) {
            std::ostringstream errMsg;
            errMsg << "Error receiving from socket: " << WSAGetLastError();
            throw std::runtime_error(errMsg.str());
        }
        if (numRecvd == 0) break;
        pbuf += numRecvd;
        buflen -= numRecvd;
        total += numRecvd;
    }

    return total;
}

void readAll(SOCKET sckt, void *buf, int buflen)
{
    // read all bytes until buflen has been received,
    // or an error occurs...

    if (readSome(sckt, buf, buflen) != buflen)
        throw std::runtime_error("Socket disconnected unexpectedly");
}

std::string readLine(SOCKET sckt)
{
    // read a line of characters until a line break is received...

    std::string line;
    char c;

    do
    {
        readAll(sckt, &c, 1);
        if (c == '\r')
        {
            readAll(sckt, &c, 1);
            if (c == '\n') break;
            line.push_back('\r');
        }
        else if (c == '\n') {
            break;
        }
        line.push_back(c);
    }
    while (true);

    return line;
}

...

inline void ltrim(std::string &s) {
    // erase whitespace on the left side...
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

inline void rtrim(std::string &s) {
    // erase whitespace on the right side...
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

inline void trim(std::string &s) {
    // erase whitespace on both sides...
    ltrim(s);
    rtrim(s);
}

inline void upperCase(std::string &s)
{
    // translate all characters to upper-case...
    std::transform(s.begin(), s.end(), s.begin(), ::toupper);
}

...

std::string makeRequest(const std::string &host, const std::string &method, const std::string &resource, const std::vector<std::string> &extraHeaders, const void *body, int bodyLength)
{
    std::ostringstream oss;
    oss << method << " " << resource << " HTTP/1.1\r\n";
    oss << "Host: " << host << "\r\n";
    oss << "Content-Length: " << bodyLength << "\r\n";
    for(auto &hdr : extraHeaders)
    {
        // TODO: ignore Host and Content-Length...
        oss << hdr << "\r\n";
    }
    oss << "\r\n";
    oss.write(static_cast<const char*>(body), bodyLength);
    return oss.str();
}

bool getHeaderValue(const std::vector<std::string> &headers, const std::string &headerName, std::string &value)
{
    value.clear();

    std::string toFind = headerName;
    upperCase(toFind);

    // find the requested header by name...
    for(auto &s : headers)
    {
        std::string::size_type pos = s.find(':');
        if (pos != std::string::npos)
        {
            std::string name = s.substr(0, pos-1);
            trim(name);
            upperCase(name);
            if (name == toFind)
            {
                // now return its value...
                value = s.substr(pos+1);
                trim(value);
                return true;
            }
        }
    }

    // name not found
    return false;
}

...

std::cout << "Connected to " << hostName << std::endl;

try
{
    std::string method, resource, hdr, data;
    std::string status, version, reason;
    std::vector<std::string> headers;
    int statusCode, rec;

    do
    {
        headers.clear();
        data.clear();

        // get user input

        std::cout << "Method > " << std::flush;
        if (!std::getline(std::cin, method))
            throw std::runtime_error("Error reading from stdin");
        upperCase(method);

        std::cout << "Resource > " << std::flush;
        if (!std::getline(std::cin, resource))
            throw std::runtime_error("Error reading from stdin");

        std::cout << "Extra Headers > " << std::flush;
        while (std::getline(std::cin, hdr) && !hdr.empty())
            headers.push_back(hdr);
        if (!std::cin)
            throw std::runtime_error("Error reading from stdin");

        std::cout << "Data > " << std::flush;
        // use Ctrl-Z or Ctrl-D to end the data, depending on platform...
        std::ios_base::fmtflags flags = std::cin.flags();
        std::cin >> std::noskipws;
        std::copy(std::istream_iterator<char>(std::cin), std::istream_iterator<char>(), std::back_inserter(data));
        if (!std::cin)
            throw std::runtime_error("Error reading from stdin");
        std::cin.flags(flags);
        std::cin.clear();

        // send request

        std::string request = makeRequest(hostName, method, resource, headers, data.c_str(), data.length());
        std::cout << "Sending request: << std::endl << request << std::endl;

        // TODO: reconnect to hostName if previous request disconnected...
        sendAll(connectSocket, request.c_str(), request.length());

        // receive response

        headers.clear();
        data.clear();

        // read the status line and parse it...
        status = readLine(connectSocket);
        std::cout << status << std::endl;    
        std::getline(std::istringstream(status) >> version >> statusCode, reason);
        upperCase(version);

        // read the headers...
        do
        {
            hdr = readLine(connectSocket);
            std::cout << hdr << std::endl;
            if (hdr.empty()) break;
            headers.push_back(hdr);
        }
        while (true);

        // The transfer-length of a message is the length of the message-body as
        // it appears in the message; that is, after any transfer-codings have
        // been applied. When a message-body is included with a message, the
        // transfer-length of that body is determined by one of the following
        // (in order of precedence):

        // 1. Any response message which "MUST NOT" include a message-body (such
        // as the 1xx, 204, and 304 responses and any response to a HEAD
        // request) is always terminated by the first empty line after the
        // header fields, regardless of the entity-header fields present in
        // the message.
        if (((statusCode / 100) != 1) &&
            (statusCode != 204) &&
            (statusCode != 304) &&
            (method != "HEAD"))
        {
            // 2. If a Transfer-Encoding header field (section 14.41) is present and
            // has any value other than "identity", then the transfer-length is
            // defined by use of the "chunked" transfer-coding (section 3.6),
            // unless the message is terminated by closing the connection.
            if (getHeaderValue(headers, "Transfer-Encoding", hdr))
                upperCase(hdr);
            if (!hdr.empty() && (hdr != "IDENTITY"))
            {
                std::string chunk;
                std::string::size_type oldSize, size;

                do
                {
                    chunk = readLine(connectSocket);

                    std::istringstream(chunk) >> std::hex >> size;
                    if (size == 0) break;

                    oldSize = data.size();
                    chunkData.resize(oldSize + size);
                    readAll(connectSocket, &data[oldSize], size);
                    std::cout.write(&data[oldSize], size);

                    readLine(connectSocket);
                }
                while (true);

                std::cout << std::endl;

                do
                {
                    hdr = readLine(connectSocket);
                    std::cout << hdr << std::endl;
                    if (hdr.empty()) break;
                    headers.push_back(hdr);
                }
                while (true);
            }

            // 3. If a Content-Length header field (section 14.13) is present, its
            // decimal value in OCTETs represents both the entity-length and the
            // transfer-length. The Content-Length header field MUST NOT be sent
            // if these two lengths are different (i.e., if a Transfer-Encoding
            // header field is present). If a message is received with both a
            // Transfer-Encoding header field and a Content-Length header field,
            // the latter MUST be ignored.
            else if (getHeaderValue(headers, "Content-Length", hdr))
            {
                std::string::size_type size;
                if ((std::istringstream(hdr) >> size) && (size > 0))
                {
                    data.resize(size);
                    readAll(connectSock, &data[0], size);
                    std::cout << data;
                }
            }

            // 4. If the message uses the media type "multipart/byteranges", and the
            // transfer-length is not otherwise specified, then this self-
            // delimiting media type defines the transfer-length. This media type
            // MUST NOT be used unless the sender knows that the recipient can parse
            // it; the presence in a request of a Range header with multiple byte-
            // range specifiers from a 1.1 client implies that the client can parse
            // multipart/byteranges responses.
            else if (getHeaderValue(headers, "Content-Type", hdr) &&
                    (hdr.compare(0, 10, "multipart/") == 0))
            {
                // TODO: extract 'boundary' attribute and read from
                // socket until the terminating boundary is reached...
            }

            // 5. By the server closing the connection.
            else
            {
                do
                {
                    rec = readSome(connectSocket, recvBuf, sizeof(recvBuf));
                    if (rec == 0) break;
                    data.append(recvBuf, rec);
                    std::cout.write(recvBuf, rec);
                }
                while (rec == sizeof(recvBuf));
            }
        }

        std::cout << std::endl;

        // use status, headers, and data as needed ...

        getHeaderValue(headers, "Connection", hdr);
        upperCase(hdr);

        if (version == "HTTP/1.0")
        {
            if (hdr != "KEEP-ALIVE")
                break;
        }
        else
        {
            if (hdr == "CLOSE")
                break;
        }
    }
    while (true);
}
catch (const std::exception &e)
{
    std::cerr << e.what() << std::endl;
}

closesocket(connectSocket);
std::cout << "Disconnected from " << hostName << std::endl;

std::system("pause");

HTTP不是很有趣吗? :-)到目前为止,这不是一个完整的HTTP实现,但它应该让你开始。但是,正如您所看到的,从头开始实施HTTP可能非常复杂,并且您必须遵循许多规则和限制。最好不要手动实现HTTP。有许多可用于C ++的第三方HTTP库。使用其中一个,让他们为您处理艰苦的工作,这样您就可以专注于自己的业务逻辑。