Winsock2.h 无法发送 http 请求

Winsock2.h can't send http requests

本文关键字:http 请求 Winsock2      更新时间:2023-10-16

称呼程序员,

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

目前,我的目标是向网页发送HTTP请求。它连接良好。但是当 while 循环运行时,立即通过 cin.getline 过程发送一些东西,而无需我输入任何内容。我觉得这很奇怪,但无论如何它似乎都是工作。

每次我发送类似"GET/HTTP/1.1\r\r"的内容时,它都会返回正确的内容,但是我输入的任何其他内容,例如"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");
}

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

您显示的代码不会尝试实现HTTP协议的任何外观,甚至没有接近。

首先,如果你更仔细地看你自己的例子,你会发现GET请求(顺便说一句,由于您使用 HTTP 1.1,缺少必需的Host标头(包含 2 个换行符,但cin.getline()(为什么不std::getline()?(一次只读取 1 行。 因此,您在一行中阅读并发送它,然后等待由于您尚未完成发送完整请求而未到达的响应。 这可以解释为什么你的while循环挂起。

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

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

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

一般来说,您在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.1rn";
oss << "Host: " << host << "rn";
oss << "Content-Length: " << bodyLength << "rn";
for(auto &hdr : extraHeaders)
{
// TODO: ignore Host and Content-Length...
oss << hdr << "rn";
}
oss << "rn";
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。有很多第三方HTTP库可用于C++。 改用其中之一,让他们为您处理艰苦的工作,这样您就可以专注于自己的业务逻辑。