有限状态机

  有限状态机——逻辑单元内部的一种高效编程方法。

  有的应用层协议头部包含数据包类型字段,每种数据类型可以映射为逻辑单元的一种执行状态,服务器可以根据它来编写相应的处理逻辑

独立的有限状态机

  该状态机的每个状态都是相互独立的,状态之间没有相互转移。

 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;
}

 

posted on 2019-04-27 22:50  tianzeng  阅读(586)  评论(0编辑  收藏  举报

导航