epoll+thread pool 服务器简单实例

来源:互联网 发布:中点画圆算法流程图 编辑:程序博客网 时间:2024/05/17 10:40
服务器首先必须有一个监听套接字,然后用epoll去监听这个套接字,如果有连接到来,调用accept函数接受此连接,然后将此连接的套接字描述符传递到消息队列中,然后线程池检查消息队列,有任务,激活线程池中的一个线程,执行此任务。上述为我们服务器的基本的流程,下面具体的是实现。首先先介绍几个包裹函数,也就是socket,bind,listen和accept函数的包裹函数,里面只是添加了一些错误处理。
 int Socket_t(){    int fd=socket(AF_INET,SOCK_STREAM,0);    if(fd==-1)    {        perror("The socket func error : ");        exit(1);    }    return fd;}

该函数返回一个TCP流套接字,正确执行,返回一个套接字描述符。

void Bind(int fd,const struct sockaddr_in * saddr,socklen_t len){    int num=bind(fd,(struct sockaddr*)saddr,len);    if(num==-1)      perror("The bind func error : ");}
Bind函数完成绑定工作,参数分别为套接字描述符,ipv4地址以及地址长度。该函数无返回值,出错打印错误信息。
void Listen(int fd,int backlog){    int num=listen(fd,backlog);    if(num==-1)      perror("The listen func error : ");}
此函数参数和listen函数完全相同,完成监听工作。
int Accept(int fd){    int num=accept(fd,NULL,NULL);    if(num==-1)    {        perror("The accept func error : ");        exit(1);    }    return num;}
接受一个连接,并返回一个该连接的套接字描述符。
void non_blocking(int fd){    int flags;    flags=fcntl(fd,F_GETFL,0);    flags |=O_NONBLOCK;    fcntl(fd,F_SETFL,flags);}
这个函数将指定的描述符模式改为非阻塞模式。我们的服务器需要一个消息队列,而C语言没有C++现成的queue模板来使用,需要我们自己来写一个。我写的一个比较简单的队列来充当消息队列。
#define MAX_LINK 20typedef struct Queue{    int start;    int end;    int fd[MAX_LINK];}Queue;
上面为我们队列的结构体,其中start代表队列尾,end代表队列头,我用的是数组实现的一个循环队列。其中数组大小为20下面是一些对应的队列的操作。
void queue_init(Queue *queue)//队列初始化{    queue->start=0;    queue->end=0;    int i=0;    bzero(queue->fd,sizeof(queue->fd));}void queue_add(Queue*queue,int fd)//队列添加数据{    int n=(queue->start+1)%MAX_LINK;    if(n==queue->end)    {        printf("The queue was filled\n");        return;    }    else    {        queue->start=n;        queue->fd[queue->start]=fd;    }}int queue_pop(Queue *queue)//队列弹出一个数据{    if(queue->end==queue->start)    {        printf("The queue is empty\n");        return -1;    }       else    {        queue->end=(queue->end+1)%MAX_LINK;        return queue->fd[queue->end];    }}int queue_size(Queue*queue)//队列当前大小{    return queue->start-queue->end;}
有了队列之后,我们还需要一个线程池。下面为线程池的操作和结构。
#define MAX_P_NUM 3typedef struct Pool{    pthread_mutex_t mutex;    pthread_cond_t cond;    pthread_t pid[MAX_P_NUM];    Queue *queue;}Pool;
上面为线程池的结构体,mutex和cond分别为互斥锁和条件变量,MAX_P_NUM代表线程池所拥有的总线程数,queue代表消息队列。
void pool_init(Pool*pool,Queue*queue)//线程初始化{    pthread_mutex_init(&pool->mutex,NULL);    pthread_cond_init(&pool->cond,NULL);    bzero(pool->pid,sizeof(pool->pid));    pool->now_work=0;    pool->queue=queue;    int i=0;    for(i=0;i<MAX_P_NUM;i++)    {        int err=pthread_create(&(pool->pid[i]),NULL,p_func,(void*)pool);        if(err!=0)        {            fprintf(stderr,"The Thread %d create failed\n",i);            return;        }    }}void pool_addw(Pool* pool,int fd)//线程池中增加任务{    queue_add(pool->queue,fd);    pthread_cond_signal(&(pool->cond));}void *p_func(void *arg)  //线程处理函数{    Pool *pool=(Pool*)arg;    while(1)    {        while(queue_size(pool->queue)==0)        {            pthread_mutex_lock(&(pool->mutex));            pthread_cond_wait(&(pool->cond),&(pool->mutex));        }        int fd=queue_pop(pool->queue);        pthread_mutex_unlock(&(pool->mutex));        char rbuf[20];        bzero(rbuf,sizeof(rbuf));        while(recv(fd,rbuf,sizeof(rbuf),0)!=0)        {            printf("%lx recv string %s\n",pthread_self(),rbuf);            bzero(rbuf,sizeof(rbuf));        }    }}
以上就是我们的准备工作,下面就是服务器的代码。
#include<sys/socket.h>#include<sys/types.h>#include<arpa/inet.h>#include<stdlib.h>#include<stdio.h>#include<assert.h>#include<errno.h>#include<string.h>#include<fcntl.h>#include<sys/epoll.h>#include<unistd.h>#include<signal.h>#include"Queue.h"#include"Pool.h"Queue queue;  //消息队列Pool pool;    //线程池int main(int argc,char **argv){    queue_init(&queue);  //消息队列初始化    pool_init(&pool,&queue); //线程池初始化    struct sockaddr_in seraddr;    int lisfd,confd;    seraddr.sin_family=AF_INET;    seraddr.sin_port=htons(6000);    seraddr.sin_addr.s_addr=htonl(INADDR_ANY);    lisfd=Socket_t();    assert(lisfd>0);    Bind(lisfd,&seraddr,sizeof(seraddr));    Listen(lisfd,5);    non_blocking(lisfd);    //到这里我们的监听套接字创建完毕    signal(SIGPIPE,SIG_IGN);//让系统忽略SIGPIPE信号    int efd=epoll_create(50);//    assert(efd>0);    struct epoll_event ev;    ev.data.fd=lisfd;    ev.events=EPOLLIN | EPOLLET;    int ctl_num=epoll_ctl(efd,EPOLL_CTL_ADD,lisfd,&ev);    struct epoll_event rev[20];    while(1)    {        bzero(rev,sizeof(rev));        int n=epoll_wait(efd,rev,20,-1);        int i;        for(i=0;i<n;i++)        {            if(rev[i].data.fd==lisfd)            {                confd=Accept(lisfd);                printf("the confd is %d\n",confd);                if(confd>0)                    pool_addw(&pool,confd);            }        }    }    return 0;}
客户端代码这里就不再贴出来了,客户端每隔一秒发送一个"hello"字符串,服务器接收并打印他。
0 0
原创粉丝点击