#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
//主状态机有两种状态,当前正在分析请求行,当前正在分析头部字段
enum CHECK_STATE{CHECK_STATE_REQUESTLINE = 0,CHECK_STATE_HEADER};
//从状态机三种状态,读取完整一行,行出错,行数据读取不完整
enum LINE_STATUS{ LINE_OK = 0,LINE_BAD,LINE_OPEN};
/*服务器处理结果:NO_REQUEST表示请求不完整,需要继续读取客户数据;GET_REQUEST表示获得了一个完整的客户端请求;
BAD_REQUEST表示客户请求有语法错误;FORBIDDEN_REQUEST表示客户对资源没有足够的访问权限;INTERNAL_ERROR表示服务器内部错误;
CLOSE_CONNECTION表示客户端已关闭连接*/
enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,FORBIDDEN_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
//从状态机解析出一行的内容,buf指向缓冲区,check_index指向当前正分析的字节,read_index指向缓冲区数据尾部的下一个字节
LINE_STATUS parse_line(char* buf,int& check_index,int& read_index)
{
char temp;
for(;check_index < read_index;check_index++)
{
//获得当前要分析的字节
temp = buf[check_index];
if(temp == '\r')
{
if((check_index + 1) == read_index)
{
return LINE_OPEN;
}
else if(buf[check_index + 1] == '\n')
{
//将\r\n转换为\0方便后续处理
buf[check_index++] = '\0';
buf[check_index++] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
else if(temp == '\n')
{
if((check_index > 0) && buf[check_index - 1] == '\r')
{
//将\r\n转换为\0方便后续处理
buf[check_index - 1] = '\0';
buf[check_index++] = '\0';
return LINE_OK;
}
return LINE_BAD;
}
}
return LINE_OPEN;
}
//分析请求行
HTTP_CODE parse_requestline(char *temp,CHECK_STATE& checkstate)
{
char *url = strpbrk(temp," \t");//返回temp字符串中第一个出现空白字符或者'/t'字符的位置
if(!url)//若请求行中没有空白字符或者'/t'字符,则http请求必有问题
{
return BAD_REQUEST;
}
*url++ = '\0';
//分析请求方法是否为GET方法
char *p_1 = temp;
if(strcasecmp(p_1,"GET") == 0)//忽略大小写比较字符串
{
printf("The request method is GET\n");
}
else return BAD_REQUEST;
url += strspn(url," \t");//清除前面多余的空格
char *p_2 = strpbrk(url," \t");
if(!p_2)
{
return BAD_REQUEST;
}
*p_2++ = '\0';
//分析协议版本字段的正确性
p_2 += strspn(p_2," \t");
if(strcasecmp(p_2,"HTTP/1.1") == 0)
{
printf("协议版本为:%s\n",p_2);
}
else return BAD_REQUEST;
//分析url的正确性
if(strncasecmp(url,"http://",7) == 0)
{
url += 7;
url = strchr(url,'/');
}
if(!url || url[0] != '/')//若http://后面没有/字符或者url前七个字节不是"http://",则语法错误
{
return BAD_REQUEST;
}
printf("The request URL is: %s\n",url);
//HTTP请求行处理完毕,状态转移到头部字段进行分析
checkstate = CHECK_STATE_HEADER;
return NO_REQUEST;
}
//分析头部字段
HTTP_CODE parse_headers(char *temp)
{
if(temp[0] == '\0')//若出现空行,则得到了一个正确的http请求
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 can not handle this header\n");
}
return NO_REQUEST;
}
/*分析http请求入口函数*/
HTTP_CODE parse_content(char *buf,int &check_index,int &read_index,CHECK_STATE& checkstate,int& start_line)
{
LINE_STATUS linestatus = LINE_OK; //当前行的读取状态
HTTP_CODE httpcode; //http请求的处理状态
while( (linestatus = parse_line(buf,check_index,read_index)) == LINE_OK) //读取一行数据
{
char *temp = buf + start_line; //定位行在buf中的位置
start_line = check_index; //更新行的位置
switch(checkstate)
{
case CHECK_STATE_REQUESTLINE: //第一个状态分析请求行
{
httpcode = parse_requestline(temp,checkstate);
if(httpcode == BAD_REQUEST)
return BAD_REQUEST;
break;
}
case CHECK_STATE_HEADER: //第二个状态,分析头部字段
{
httpcode = parse_headers(temp);
if(httpcode == 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 < 3)
{
printf("usage: %s ip port\n",argv[0]);
exit(1);
}
char* ip = argv[1];
int port = atoi(argv[2]);
int listenfd,clifd;
struct sockaddr_in ser_addr,cli_addr;
socklen_t cli_len = sizeof(cli_addr);
listenfd = socket(AF_INET,SOCK_STREAM,0);
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(port);
inet_pton(AF_INET,ip,&ser_addr.sin_addr);
bind(listenfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
listen(listenfd,3);
clifd = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_len);
if(clifd == -1)
{
perror("accept error:");
exit(1);
}
char buf[BUFSIZ];
int read_num = 0;
int read_index = 0;//当前已经读入多少字节的客户数据
int check_index = 0;//当前已经分析完了多少字节的客户数据
int start_line = 0;//行在buf中的起始位置
CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;//设置主状态机的初始状态
while(1)//循环读取数据并分析
{
read_num =read(clifd,buf+read_index,BUFSIZ-read_index);
if(read_num == -1)
{
perror("read error:");
break;
}
else if(read_num == 0)
{
printf("remote client has closed the connection\n");
break;
}
read_index += read_num;
//分析目前已经获得的所有数据
HTTP_CODE httpcode = parse_content(buf,check_index,read_index,checkstate,start_line);
if(httpcode == NO_REQUEST) //没有获得完整的数据
{
continue;
}
else if(httpcode == GET_REQUEST)//得到一个完整的、正确的HTTP请求
{
printf("we get a GET_REQUEST\n");
break;
}
else{ //其他情况为发生错误
printf("we get a bad_request\n");
break;
}
}
close(clifd);
close(listenfd);
return 0;
}
用有限状态机处理http请求
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.