HTTP协议(关于HTTP协议之前的文章有写过)如何解析or封装?
首先来看下这块的处理过程,有一个整体的概念:
void Http::process() {
//主线程读完数据后,来到这里处理
HTTP_CODE read_ret = process_read();
if (read_ret == NO_REQUEST) {
//解析完数据发现客户端没有请求,直接退出
return ;
}
//如果有请求,根据刚才解析的结果,封装报文
bool write_ret = process_write(read_ret);
if (!write_ret) {
close_conn();
}
//告诉主线程,报文已经封装好了,可以发送了!
modfd(s_iEpollfd, m_iSockFd, EPOLLOUT);
}
主线程读数据:
bool Http::read() {
if (m_read_idx >= READ_BUFFER_SIZE) {
return false;
}
int bytes_read = 0;
while (true) {
bytes_read = recv(m_iSockFd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);
if (bytes_read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
return false;
} else if (bytes_read == 0) {
return false;
}
m_read_idx += bytes_read;
}
return true;
}
读到的正是这样一行一行的数据:
将数据循环读到m_read_buf
中,接着开始处理读到的数据:
Http::HTTP_CODE Http::process_read()
{
...
while (...) {
//拿到每一行数据
text = get_line();
switch (m_check_state) {
case CHECK_STATE_REQUESTLINE: {
//处理请求行
parse_request_line(text);
...
break;
}
case CHECK_STATE_HEADER: {
//处理头部字段
parse_headers(text);
...
break;
}
default: {
return INTERNAL_ERROR;
}
}
}
}
用一个变量m_check_state
来表示分析状态:
enum CHECK_STATE {
CHECK_STATE_REQUESTLINE = 0, //当前正在分析请求行
CHECK_STATE_HEADER //当前正在分析头部字段
};
以便在parse_request_line
之后改变状态,之后进入parse_headers
中处理。
分析请求行和头部字段,可以了解到当前客户端要请求的资源的信息以及客户端一些信息。
若在解析的过程中发现客户端请求了页面,则在服务端找到该文件,并写入内存,等待发送,比如在do_request
中
Http::HTTP_CODE Http::do_request() {
...
//若解析出的请求行中有“/”则默认返回index.html首页面
if (strcmp(m_url, "/") == 0) {
strcat(m_url, "index.html");
}
//处理其他,比如服务端没有资源,返回404.html 等等
...
//若资源存在,读到内存中,等待发送
}
解析请求行和头部字段后,发现需要返回资源,便按照响应报文的格式封装报文并发送:
bool Http::process_write(HTTP_CODE ret) {
switch (ret) {
...
case NO_RESOURCE: {
add_status_line(404, error_404_title);
add_headers(strlen(error_404_form));
add_content(error_404_form);
break;
}
case FILE_REQUEST: {
add_status_line(200, ok_200_title);
add_headers(file.size());
add_content(file);
break;
}
default: {
return false;
}
}
return true;
}
add_status_line
用来添加状态行,add_headers
用来添加头部字段(之后添加一行空行),add_content
用来添加内容。
至此,响应报文也就封装完成了,之后通知主线程发送报文即可。