1.ReadLine
- 读取的基本单位:按照行来进行读取
- 不同平台,对行分隔符的处理可能不同,所以要有一个函数可以统一处理不同平台的情况,兼容各种行分隔符
- 本函数将统一转化为
\n
结尾 - [函数实现] -> 见[Util类]实现
2.RecvRequest
- 读取出错处理:
- 每一步读取都有可能出错,只有在请求行和请求报头都正常读取的情况下,才去解析请求
void RecvRequest()
{
if ((!RecvRequestLine()) && (!RecvRequestHeader()))
{
ParseRequestLine();
ParseRequestHeader();
RecvRequestBody();
}
}
bool RecvRequestLine()
{
if (Util::ReadLine(_sock, _request.request_line) > 0)
{
_request.request_line.resize(_request.request_line.size() - 1);
LOG(DEBUG, _request.request_line);
}
else
{
_stop = true;
}
return _stop;
}
bool RecvRequestHeader()
{
std::string line;
while(true)
{
line.clear();
if(Util::ReadLine(_sock, line) <= 0)
{
_stop = true;
break;
}
if(line == "\n")
{
_request.blank = line;
break;
}
line.resize(line.size() - 1);
_request.request_header.push_back(line);
}
return _stop;
}
3.ParseRequest
- 要将请求行中的内容单独提出出来,可以用
string.substr
提取字串,但略显繁琐
stringstream
类重载了operator>>
可以将内部内容格式化输入到缓冲区中
- 此类可以使用输入和输出流上允许的任何操作从流中插入/提取字符
- 所以此处用法类似
cin
- 请求报头处,可以用
unordered_map
存储键值对,以便需要时拿取
void ParseRequestLine()
{
std::stringstream ss(_request.request_line);
ss >> _request.method >> _request.uri >> _request.version;
std::transform(_request.method.begin(), _request.method.end(), _request.method.begin(), ::toupper);
}
void ParseRequestHeader()
{
std::string key;
std::string value;
for(auto& str : _request.request_header)
{
if(Util::CutString(str, key, value, SEP))
{
_request.headerMap[key] = value;
}
}
}
4.RecvRequestBody
- GET方法通过url传参,POST方法通过正文传参**,所以是否要接收请求正文,还要判断此次请求是什么方法**
bool IsRecvRequestBody()
{
std::string& method = _request.method;
if(method == "POST")
{
_request.content_length = atoi(_request.headerMap["Content-Length"].c_str());
return true;
}
return false;
}
bool RecvRequestBody()
{
if(IsRecvRequestBody())
{
size_t length = _request.content_length;
auto& body = _request.request_body;
char ch = 'K';
while(length)
{
ssize_t s = recv(_sock, &ch, 1, 0);
if(s > 0)
{
body.push_back(ch);
length--;
}
else
{
_stop = true;
break;
}
}
LOG(DEBUG, body);
}
return _stop;
}