天天看點

有限狀态機

有限狀态機

  有限狀态機——邏輯單元内部的一種高效程式設計方法。

  有的應用層協定頭部包含資料包類型字段,每種資料類型可以映射為邏輯單元的一種執行狀态,伺服器可以根據它來編寫相應的處理邏輯

獨立的有限狀态機

  該狀态機的每個狀态都是互相獨立的,狀态之間沒有互相轉移。

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

繼續閱讀