无限读取套接字
问题描述:
从套接字读取分块数据(来自http请求)的正确方法是什么?无限读取套接字
sf::TcpSocket socket;
socket.connect("0.0.0.0", 80);
std::string message = "GET /address HTTP/1.1\r\n";
socket.send(message.c_str(), message.size() + 1);
// Receive an answer from the server
char buffer[128];
std::size_t received = 0;
socket.receive(buffer, sizeof(buffer), received);
std::cout << "The server said: " << buffer << std::endl;
但服务器发送无限数据并且socket.receive
不返回管理。任何正确的方法来逐块读取分块数据? (答案是分块数据)。
答
处理HTTP请求的正确方法是使用管理套接字连接的更高级别库。在C++中,一个例子是pion-net;还有其他人也喜欢Mongoose(这是C语言,但可以在C++中使用)。
答
好的无限数据在理论上是可能的,而实际的实现因过程而不同。
- 方法1 - 通常很多协议都在最初的几个字节(4个字节)发送的大小,你可以有一个while循环
{
int i = 0, ret = 1;
unsigned char buffer[4];
while (i<4 && ret == 0)
socket.receive(buffer + i, 1 , ret);
// have a while loop to read the amount of data you need. Malloc the buffer accordingly
}
- 方法2 - 或者你的情况下,你不知道长度(无限)
{
char *buffer = (char *)malloc(TCP_MAX_BUF_SIZE);
std::size_t total = 0, received = 0;
while (total < TCP_MAX_BUF_SIZE && return >= 0) {
socket.receive(buffer, sizeof(buffer), received);
total += received;
}
//do something with your data
}
你将不得不在somepoint打破和处理您的数据就派遣释放的另一个线程的内存。
答
如果通过“chunked data”指向Transfer-Encoding: chunked
HTTP头,则需要读取每个块并解析块头以知道每个块需要读取多少数据并知道最后一个块的时间接收。因为分块数据具有定义的结构,所以您不能盲目地致电socket.receive()
。阅读RFC 2616 Section 3.6.1了解更多详情。
你需要做更多的东西一样以下(略去了错误处理 - 不要忽略它在你真正的代码):
std::string ReadALine(sf::TcpSocket &socket)
{
std::string result;
// read from socket until a LF is encountered, then
// return everything up to, but not including, the
// LF, stripping off CR if one is also present...
return result;
}
void ReadHeaders(sf::TcpSocket &socket, std::vector<std::string> &headers)
{
std::string line;
do
{
line = ReadALine(socket);
if (line.empty()) return;
headers.push_back(line);
}
while (true);
}
std::string UpperCase(const std::string &s)
{
std::string result = s;
std::for_each(result.begin(), result.end(), toupper);
return result;
}
std::string GetHeader(const std::vector<std::string> &headers, const std::string &s)
{
std::string prefix = UpperCase(s) + ":";
for (std::vector<std::string>::iterator iter = headers.begin(), end = headers.end(); iter != end; ++iter)
{
if (UpperCase(i)->compare(0, prefix.length(), prefix) == 0)
return i->substr(prefix.length());
}
return std::string();
}
sf::TcpSocket socket;
socket.connect("0.0.0.0", 80);
std::string message = "GET /address HTTP/1.1\r\nHost: localhost\r\n\r\n";
socket.send(message.c_str(), message.length());
std:vector<std::string> headers;
std::string statusLine = ReadALine(sockeet);
ReadHeaders(socket, headers);
// Refer to RFC 2616 Section 4.4 for details about how to properly
// read a response body in different situations...
int statusCode;
sscanf(statusLine.c_str(), "HTTP/%*d.%*d %d %*s", &statusCode);
if (
((statusCode/100) != 1) &&
(statusCode != 204) &&
(statusCode != 304)
)
{
std::string header = GetHeader(headers, "Transfer-Encoding");
if (UpperCase(header).find("CHUNKED") != std::string::npos)
{
std::string extensions;
std::string_size_type pos;
std::size_t chunkSize;
do
{
line = ReadALine(socket);
pos = line.find(";");
if (pos != std::string::npos)
{
extensions = line.substr(pos+1);
line.resize(pos);
}
else
extensions.clear();
chunkSize = 0;
sscanf(UpperCase(line).c_str(), "%X", &chunkSize);
if (chunkSize == 0)
break;
socket.receive(someBuffer, chunkSize);
ReadALine(socket);
// process extensions as needed...
// copy someBuffer into your real buffer...
}
while (true);
std::vector<std::string> trailer;
ReadHeaders(socket, trailer);
// merge trailer into main header...
}
else
{
header = GetHeader(headers, "Content-Length");
if (!header.empty())
{
uint64_t contentLength = 0;
sscanf(header.c_str(), "%Lu", &contentLength);
// read from socket until contentLength number of bytes have been read...
}
else
{
// read from socket until disconnected...
}
}
}