有限状态机——逻辑单元内部的一种高效编程方法。
有的应用层协议头部包含数据包类型字段,每种数据类型可以映射为逻辑单元的一种执行状态,服务器可以根据它来编写相应的处理逻辑
独立的有限状态机
该状态机的每个状态都是相互独立的,状态之间没有相互转移。
state_machine(Package _pack) { PackageType _type=_pack.getType(); switch(_type) { case type_A: process_package_A(_pack); break; case type_B: process_package_B(_pack); break; } }
带状态转移的有限状态机
状态的转移需要状态机的内部驱动
STATE_MACHINE() { State curState=type_A; while(curState!=type_C) { Package _pack=getNewPackage();//获得一个新的数据包 switch(curState) { case type_A: process_package_state_A(_pack); curState=type_B; break; case type_B: process_package_state_B(_pack); curState=type_C; break; } } }
code
#include <sys/socket .h> #include <netinet/in.h> #include carpa/inet.h> #include <ssert.h> #include cstdio.h> #include <stdlib.h> #include <unistd.h> #include <orrno.h> #include <string.h> #include <fcnt1.h> #define BUFFER SIZB 4096/读缓冲区大小 /*主状态机的两种可能状态,分别表示当前正在分析请求行,当南正在分析头邮字段*/ enum CHECK_STATE { CRECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER }; /*从状态机的三种可能状态,即行的读取状态,分别表示:读取到一个完整的行、行出错和行数据尚目不完整*/ enum LINE_STATUS { LINE_OK=0,LINE_BAD,LINE_OPEN }; /*服务器处理了http请求的结来:NO_REQUEST表示请求不完整,需要续读取客户数据;GET_REOUEST表示获得了1个完警的客户清求; BAD_REQUEST表示客户请求有语法错误;FORBIDDEN_REOUEST表示客户对资源没有足够的访问权限;INTERNALL_ERROR表示服务器内部错误; CLOSED_CONECTION:表示家户端已经关闭连接*/ enum HTTP_CODE { NO_REQUEST,GET_REQUEST,BAD_REQUEST,FORBIDDEN_REQUEST,INTERNALL_ERROR,CLOSED_CONNECTON }; /*为了简化问题,我们没有给客户端发送一个完整的HTTP应答报文,面只是极据服务器的处理结果发送如下威功或失败信息*/ static const char* szret[]={"I get a correct result\n", "Something wrong\n" }; /*从状态机,用干解析出一行内容,初始状态为OK,原始驱动力来自于buffer中新到来的数据*/ LINE_STATUS praseLine(char* buffer,int& checked_index, int& read_index) { char temp; /* checked index指向buffer (应用程序的读缓冲区)中当前正在分析的字节。read index指向buffer中客户数据的尾部的下一字节 buffer中第0-checkedindex字节都已分析完率,第checkedindex- (read index-11 字节由下面的循环挨个分析*/ for(;checked_index<read_index;++checked_index) { /*获得当前要分析的字节*/ temp=butfer[checked_index]; /*如果当前的字节是“\r”,即到车符,则说明可能读取到一个完整的行*/ if(temp=='\r') { /*如果“\r"是buff最后一个被读入的数据,这次没有读取到完整的一行*/ if((checked_index+1)==read_index) return LINE_OPEN; else if(temp[checked_index+1]=='\n')//下一个字符是\n,读取到完整的一行 { buffer[checked_index++]='\0'; buffer[checked_index++]='\0'; return LINE_OK; } return LINE_BAD; } else if(temp=='\n') { if((checked_index-1)>0&&temp[checked_index-1]=='\r') { buffer[checked_index++]='\0'; buffer[checked_index++]='\0'; return LINE_OK; } } return LINE_BAD; } return LINE_OPEN; } HTTP_CODE praseRequestLine(char *temp,CHECK_STATE &state) { char *url=strpbrk(temp,"\t"); if(!url) { return BAD_REQUEST; } *url++='\0'; char *method=temp; if(strcasecmp(method."GET")==0) { printf("method id GET"); } else { return BAD_REQUEST; } url+=strspn(url,"\t"); char *version=strpbrk(url,"\t"); if(!version) { return BAD_REQUEST; } *version++='\0'; version+=strspn(version,"\t"); if(strcasecmp(version,"HTTP/1.1")!=0) { return BAD_REQUEST; } if(strncasecmp(url,"http://",7)==0) { url+=7; url=strchr(url,'/'); } if(!url||url[0]!='/') return BAD_REQUEST; printf("The request url is:\n",url); state=CHECK_STATE_HEADER;//解析完请求行后将其设置为CHECK_STATE_HEADER,来实现状态转移 return NO_REQUEST; } HTTP_CODE parseHeaders(char *temp) { if(temp[0]=='0') return GET_REQUEST; else if(strncasecmp(temp,"Host:",5)==0) { temp+=5; temp+=strspn(temp,"\t"); printf("the request host is: \n",temp); } else { perror("not handle this header"); } return NO_REQUEST; } //http 请求入口函数 HTTP_CODE praseContent(char *buffer,int *checkedIndex,CHECK_STATE &state,int &readIndex,int &startLine) { LINE_STATUS lineStatus=LINE_OK; HTTP_CODE resCode=NO_REQUEST; // while((lineStatuse=parseLine(buffer,checkedIndex,readIndex))==LINE_OK) { char *temp=buffer+startLine;//startLIine是在buffer中的起始位置 startLine=checkedIndex; switch(state) { case CHECK_STATE_REQUESTLINE://请求行 { resCode=parseRquestLine(temp,state); if(resCode==BAD_REQUEST) return BAD_REQUEST; break; } case CHECK_STATE_HEADER://头部字段 { resCode=parseHEADERS(temp); if(resCode==BAD_REQUEST) return BAD_REQUEST; else if(resCode==GET_REQUEST) return GET_REQUEST; break; } default: return INTERNALL_ERROR; } } //若没有读取到完整的一行,需要继续读 if(LINE_STATUS==LINE_OPEN) return NO_REQUEST; else return BAD_REQUEST; } int main() { //基本的tcp链接操作 char buffer[BUFFER_SIZE]; memset(buffer,'\0',BUFFER_SIZE); int dataRead=0; int readIndex=0;//当前已经读取了多少个字节的客户数据 int checkedIndex=0;//当前已经分析了多少个客户字节的数据 int startLine=0;//行在buffer中的起始位置 //设置主机的初始状态,表示当前状态为请求行,解析完请求行后变为CHECK_STATE_HEADER实现状态转移 CHECK_STATE state=CHECK_STATE_REQUESTLINE; while(true) { dataRead=recv(fd,buffer+readIndex,BUFFER_SIZE-readIndex,0); if(dataRead==-1) { perror("read failed"); break; } else if(dataRead==0) { perror("remote client has closed the connection"); break; } readIndex+=dataRead; //读取数据成功后,分析目前已经得到的数据 HTTP_CODE res=parseContent(buffer,checkedIndex,state,readIndex,starLine); if(res==NO_REQUEST) { continue; } else if(res==GET_REQUEST) { send(fd,szret[0],strlen(szret[0]),0); } else//error { send(fd,szret[1],strlen(szret[1]),0); break; } close(fd); } close(listenfd); return 0; }