18.3.3 SHTTPD的多客户端支持的实现

本文介绍SHTTPD服务器如何通过Worker_ScheduleRun()和Worker_ScheduleStop()函数实现多客户端支持。主要包括业务线程的初始化、客户端连接处理、线程状态控制及资源释放等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

18.3.3  SHTTPD的多客户端支持的实现

服务器SHTTPD的多客户端支持框架的函数主要为Worker_ScheduleRun()和函数Worker_ScheduleStop(),这两个函数通过对结构struct worker_opts进行管理来控制线程的状态。结构struct worker_opts的原型如下:

 

struct worker_opts{

    pthread_t th;                   /*线程的ID*/

    int flags;                      /*线程状态*/

    pthread_mutex_t mutex;          /*线程任务互斥*/

    struct worker_ctl *work;        /*本线程的总控结构*/

};

 

结构中的成员变量flags用于表示线程所处的状态,成员mutex用于控制对本线程互斥区成员的访问,th为线程建立时的线程ID号。

Worker_ScheduleRun()函数初始化多个处理客户端请求的业务线程,并侦听客户端的连接请求,当有连接到来的时候,查询业务处理线程,将此客户端分配给业务处理线程。

1)首先初始化业务处理线程。

 

/*主调度过程,

*   当有客户端连接到来的时候

*   将客户端连接分配给空闲客户端

*   由客户端处理到来的请求

*/

int Worker_ScheduleRun(int ss)

{

    DBGPRINT("==>Worker_ScheduleRun/n");

    struct sockaddr_in client;

    socklen_t len = sizeof(client);

    /*初始化业务线程*/

    Worker_Init();

   

    int i = 0;

 

2)当调度状态SCHEDULESTATUSSTATUS_RUNNING的时候,select()等待客户端的连接。当有客户端连接到来的时候,查找空闲的业务线程,如果没有则增加一个业务线程,然后将此任务分配给找到的空闲业务线程。

 

    for(;SCHEDULESTATUS== STATUS_RUNNING;)

    {

        struct timeval tv;                                  /*超时时间*/

        fd_set rfds;                                        /*读文件集*/

        int retval = -1;

        /*清空读文件集,将客户端连接描述符放入读文件集*/

        FD_ZERO(&rfds);

        FD_SET(ss, &rfds); 

        tv.tv_sec = 0;                                      /*设置超时*/

        tv.tv_usec = 500000;

       

        retval = select(ss + 1, &rfds, NULL, NULL, &tv);    /*超时读数据*/

        switch(retval)

        {

            case -1:                                        /*错误*/

            case 0:                                         /*超时*/

                continue;

                break;

            default:

                if(FD_ISSET(ss, &rfds))                     /*检测文件*/

                {

                    int sc = accept(ss, (struct sockaddr*)&client, &len);

                    i = WORKER_ISSTATUS(WORKER_IDEL);   /*查找空闲业务线程*/

                    if(i == -1)                         /*没有找到*/

                    {

                        /*是否到达最大客户端数*/

                        i =  WORKER_ISSTATUS(WORKER_DETACHED);

                        if(i != -1)         /*没有,增加一个业务处理线程*/

                            Worker_Add( i);

                    }

                    if(i != -1)             /*业务处理线程空闲,分配任务*/

                    {

                        wctls[i].conn.cs = sc;          /*套接字描述符*/

                        pthread_mutex_unlock(&wctls[i].opts.mutex);
                                            /
*告诉业务线程有任务*/

                    }              

                }

        }      

    }

   

    DBGPRINT("<==Worker_ScheduleRun/n");

    return 0;

}

 

3Worker_ScheduleStop()函数用于终止业务处理线程和分配任务线程。它先将控制分配任务流程的变量SCHEDULESTATUS设置为STATSU_STOP,保证分配业务线程不再分配任务;然后给所有的业务处理线程发送终止命令,等待所有的业务线程退出,当所有的业务线程销毁之后,释放资源并退出。

 

/*停止调度过程*/

int Worker_ScheduleStop()

{

    DBGPRINT("==>Worker_ScheduleStop/n");

    SCHEDULESTATUS = STATSU_STOP;           /*给任务分配线程设置终止条件*/

    int i =0;

    Worker_Destory();                       /*销毁业务线程*/

    int allfired = 0;

    for(;!allfired;)                        /*查询并等待业务线程终止*/

    {

        allfired = 1;

        for(i = 0; i<conf_para.MaxClient;i++)

        {

            int flags = wctls[i].opts.flags;

            if(flags == WORKER_DETACHING || flags == WORKER_IDEL)

                                            /*线程正活动*/

                allfired = 0;

        }

    }

   

    pthread_mutex_destroy(&thread_init);    /*销毁互斥变量*/

    for(i = 0; i<conf_para.MaxClient;i++)   /*销毁业务处理线程的互斥*/

        pthread_mutex_destroy(&wctls[i].opts.mutex);

    free(wctls);                            /*销毁业务数据*/

   

    DBGPRINT("<==Worker_ScheduleStop/n");

    return 0;

}

 

业务处理线程的维护有业务处理线程初始化、业务处理线程销毁、业务处理线程增加和业务处理线程删除等。负责初始化、增加、删除和销毁功能,并且有查询业务处理线程状态的函数,方便控制业务线程。

4)初始化业务线程的函数为Worker_Init(),负责申请维护全部业务和线程状态的结构struct worker_ctl,个数为配置文件中设置的最大客户端数,每一个均表示一个线程:

 

/*初始化线程*/

static void Worker_Init()

{

    DBGPRINT("==>Worker_Init/n");

    int i = 0;

   

    wctls = (struct worker_ctl*)malloc(sizeof(struct worker_ctl)*conf_para.
    MaxClient);                                           /
*初始化总控参数*/

    memset(wctls, 0, sizeof(*wctls)*conf_para.MaxClient);/*清零*/

 

结构定义如下,其中的成员opts用于表示线程的状态,成员conn表示客户端请求的状态和值:

 

struct worker_ctl{

    struct worker_opts opts;

    struct worker_conn conn;

};

 

其中的结构struct worker_conn用于维护客户端请求和响应数据,结构如下所示。其中的dreq存放接收到的客户端请求数据,dres存放发送给客户端的数据,cs为与客户端连接的套接字描述符,to表示超时响应时间,con_res维护响应结构,con_req维护客户端的      请求。

 

struct worker_conn

{

#define K 1024

    char        dreq[16*K];             /*请求缓冲区*/

    char        dres[16*K];             /*响应缓冲区*/

    int         cs;                     /*客户端套接字文件描述符*/

    int         to;                     /*客户端无响应时间超时退出时间*/

    struct conn_response con_res;       /*响应结构*/

    struct conn_request con_req;        /*请求结构*/

    struct worker_ctl *work;            /*本线程的总控结构*/

};

 

5)然后开始初始化结构struct worker_ctl的值,例如设置线程的默认状态为WORKER_DETACHED,表示可以建立线程挂接到此结构上,初始化互斥量并调用函数pthread_mutex_lock锁定互斥区。还完成各部分的回指针,方便之后的调用,主要有控制结构opts中指向总控结构的指针,请求结构中指向连接的指针,响应结构中指向连接的指针。

 

/*初始化一些参数*/

for(i = 0; i<conf_para.MaxClient;i++)

{

    /*opts&conn结构与worker_ctl结构形成回指针*/

    wctls[i].opts.work = &wctls[i];

    wctls[i].conn.work = &wctls[i];

    /*opts结构部分的初始化*/

    wctls[i].opts.flags = WORKER_DETACHED;

    //wctls[i].opts.mutex = PTHREAD_MUTEX_INITIALIZER;

    pthread_mutex_init(&wctls[i].opts.mutex,NULL);

    pthread_mutex_lock(&wctls[i].opts.mutex);

    /*conn部分的初始化*/

    /*con_req&con_resconn结构形成回指*/

    wctls[i].conn.con_req.conn = &wctls[i].conn;

    wctls[i].conn.con_res.conn = &wctls[i].conn;

    wctls[i].conn.cs = -1;/*客户端socket连接为空*/

    /*con_req部分初始化*/

    wctls[i].conn.con_req.req.ptr = wctls[i].conn.dreq;

    wctls[i].conn.con_req.head = wctls[i].conn.dreq;

    wctls[i].conn.con_req.uri = wctls[i].conn.dreq;

    /*con_res部分初始化*/

    wctls[i].conn.con_res.fd = -1;

    wctls[i].conn.con_res.res.ptr = wctls[i].conn.dres;

}

 

在初始化参数完毕后,建立多个业务线程,其个数由配置时指定,即初始化客户端       数量。

 

    for(i = 0; i<conf_para.InitClient;i++)

    {

        /*增加规定个数工作线程*/

        Worker_Add(i);

    }

    DBGPRINT("<==Worker_Init/n");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值