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
用来添加内容。
至此,响应报文也就封装完成了,之后通知主线程发送报文即可。