使用状态机完成对HTTP请求的读取和分析

使用状态机完成对HTTP请求的读取和分析

主状态机作用(parse_content()):读取请求行与头部行数据
状态:请求行、头部行
从状态机作用(parse_line):读取每一行数据
状态:读取成功、失败、需要继续读取
parse_request():读取请求行,并将主状态机状态更改为头部行
parse_header():读取头部行

主状态机初始状态为请求行,
循环调用parse_line,若为成功状态继续(失败退出返回失败原因)
并且判断主状态机状态
请求->parse_request()->继续循环(失败退出返回失败原因)
头部->parse_header()->成功返回好的请求结果(失败退出返回失败原因)

//http请求的读取和分析
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include#define BUFFER_SIZE 4096//主状态机可能的状态
enum CHECK_STATE
{CHECK_STATE_REQUESTION = 0,//正在分析当前请求行CHECK_STATE_HEADER//正在分析头部字段
};//从状态机可能的状态
enum LINE_STATUS
{LINE_OK = 0,//读取到一个完整的行LINE_BAD,//行出错LINE_OPEN//行数据尚且不完整
};//服务器处理http请求的结果
enum HTTP_CODE
{NO_REQUEST,//请求不完整需要继续读取GET_REQUEST,//得到了一个完整的请求BAD_REQUEST,//请求有语法错误FORBIDDEN_REQUEST,//没有足够的权限INTERNAL_ERROR,//服务器内部错误CLOSED_CONNECTION//客户端连接已关闭
};static const  char* szret[] = 
{"i get a corret result\n","Sonmething wrong\n"
};//从状态机,用于解析出一行内容
LINE_STATES parse_line(char * buffer, int & checked_index, int & read_index)
{char temp;for( ; checked_index < read_index; ++checked_index){//获得当前分析的字temp = buffer[checked_index];//如果当前的字节是\r,即回车符,则说明可能读取到一个完整的行if(temp == '\r'){//如果\r字符碰巧是目前buffer中的最后一个已经被读入的客户数据,那么这次分析没有读取到一个完整的行,需要继续读取数据if(check_index + 1 == read_index)return LINE_OPEN;//表示读到了一个完整的行if(temp == '\n'){buffer[check_index++] == '\0';buffer[check_index++] == '\0';return LINE_OK;}//以上都不是,就是存在语法错误return LINE_BAD;}//当前字符为\n也有可能是到了一行的情况else if(temp == '\n'){//因为\r\n一起用,还得判断if(check_index > 1 && buffer[check_index - 1] == '\r'){buffer[check_index--] == '\0';buffer[check_index++] == '\0';return LINE_OK;}return LINE_BAD;}}//如果所有的字符都读完了还没有遇到return LINE_OPEN;}//分析请求行
HTTP_CODE parse_requestline(char * temp, CHECK_STATE & checkstate)
{/*temp|GET /chapter/user.html HTTP/1.1\0  (尾部的\0是从状态机加的)*//*url|GET /chapter/user.html HTTP/1.1\0  */char* url = strpbrk(temp," \t");//strpbrk函数比较两个字符串中是否有相同的字符,有的话返回第一个相同字符的指针if(!url)//没有空格或者\t肯定有问题的不{return BAD_REQUEST;}*url++ = '\0';/*url|GET\0/chapter/user.html HTTP/1.1\0  *//*mth url|    |GET\0/chapter/user.html HTTP/1.1\0  */char * method = temp;if(strcasecmp(method, "GET") == 0)//仅支持GET方法,strcasecmp函数不计大小写判等{printf("The request method is GET\n");}  else{return BAD_REQUEST;}//直接跳到HTTP版本/*url|GET\0/chapter/user.html HTTP/1.1\0  */url += strspn(url," \t");//该函数返回 str1 中第一个不在字符串 str2 中出现的字符下标char * version = strpbrk(url," \t");/*url             version|                 |GET\0/chapter/user.html HTTP/1.1\0  */if(!version){return BAD_REQUEST;}*version++ = '\0';//这样就把请求行解析成了三块,每一块用\0分割/*mth url               version|    |                   |GET\0/chapter/user.html\0HTTP/1.1\0*/version += strspn(version," \t");//防止\0后面还有空格或者制表符,清理一下if(strcasecmp(version,"http/1.1") != 0){return BAD_REQUEST;}//检查url是否合法if(strncasecmp(url,"https//:",7) == 0)//stncasecmp比较前n个字符,与strcasecmp还是有点区别{url += 7;url = strchr(url,'/');//在参数url所指向的字符串中搜索第一次出现字符'/'(一个无符号字符)的位置}if(!url || url[0] != '/'){return BAD_REQUEST;}pritnf("The requset URL is: %s\n",url);//http请求行处理完毕,转台转移到头部字段的分析checkstate = CHECK_STATE_HEAD;return NO_REQUEST;}//分析头部字段
HTTP_CODE parse_headers(char * temp)
{//遇到一个空行,说明我们得到了一个正确的HTTP请求if(temp[0] == '\0'){return GET_REQUEST;}else if(strncasecmp(temp, "host:",5) == 0)//只处理host请求{temp += 5;temp += strspn(temp," \t");//确认没有无效字段printf("the request host is : %s\n",temp);}else{//其他字段均不处理printf("I cannot handle this header!\n");}return NO_REQUEST;
}//分析HTTP请求的入口函数
HTTP_CODE parse_content(char * buffer, int & checked_index, CHECK_STATE & checkstate, int & read_index, int& start_line)
{LINE_STATUS linestatus = LINE_OK;//记录当前行的读取状态HTTP_CODE retcode = NO_REQUEST;//记录HTTP请求的处理结果//主状态机,用于从buffer中读取出所有完整的行while((linestatus = parse_line(buffer,checked_index,read_index)) == LINE_OK){char* temp = buffer + start_line;start_line = checked_index;//checkstate记录主状态机当前状态switch (checkstate){case CHECK_STATE_REQUESTION://分析请求行{retcode = parse_requestline(temp,checkstate);if(retcode == BAD_REQUEST){return BAD_REQUEST;}break;}case CHECK_STATE_HEADER://分析头部字段   {retcode = parse_headers(temp);if(retcode == BAD_REQUEST){return BAD_REQUEST;}if(retcode == GET_REQUEST){return GET_REQUEST;}break;}default:{return INTERNAL_ERROR;}               }}//若没有读取到一个完整的行,则表示还有需要继续读取客户数据才能进一步分析if(linestatus == LINE_OPEN){return NO_REQUEST;}else{return BAD_REQUEST;}}int main(int argc, char* argv[])
{if(argc < 2){printf("usage:%s ip_address port_number\n",basename(argv[0]));return 1;}const char * ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in address;bzero(&address,sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int listenfd = socket(AF_INET, SOCK_STREAM, 0);assert(listen >= 0);int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));assert(ret != -1);ret = listen(listenfd,128);assert(ret != -1);//创建客户端链接struct sockaddr_in client;socklen_t client_len = sizeof(client);int fd = accept(listenfd,(struct sockaddr*)&client,&client_len);if(fd < 0){pritnf("error is %d\n",errno);}else{char buffer[BUFFER_SIZE];memset(buffer,'\0',BUFFER_SIZE);int data_read = 0;int read_index = 0;//当前读了多少字节的客户数据int checked_index = 0;//当前已经分析完了多少客户的字节数据int start_line = 0;//行在buffer中的起始位置//设置主状态机的初始位置CHECK_STATE checkstate = CHECK_STATE_REQUESTION;while(1){data_read = recv(fd, buffer + read_index, BUFFER_SIZE - read_index, 0);if(data_read == -1){printf("reading fail!\n");break;}else if(data_read == 0){pritnf("client close connect!\n");break;}read_index += data_read;//分析获得的数据HTTP_CODE result = parse_content(buffer,checked_index,checkstate,read_index,start_line);if(result == NO_REQUEST){continue;}else if(result == GET_REQUEST){send(fd, szret[0], strlen(szret[0]), 0);break;}else{send(fd, szret[1], strlen(szret[1]), 0);break;}} close(fd);}close(listenfd);return 0;}

参考《linux高性能服务器编程》-游双


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部