博主從memcahced剝離了基於線程池的收發包框架代碼,linux下直接make編譯可運行:https://github.com/donaldhuang/memcached_network
用libevent做事件監聽使用起來非常簡單:
event_flags=EV_READ | EV_PERSIST;//監聽可讀事件
base=(event_base*)event_init();//初始化
event_set(event, sfd, event_flags, base_event_handler,argvs);
//創建監聽sfd句柄的可讀事件,處理函數是base_event_handler,argvs是傳入處理函數的參數
event_base_set(base, event);//爲創建的事件event指定事件基地event_add(event, time_tv);//把事件加入到事件基地進行監聽,time_tv爲epoll沒有可讀事件的超時時間
event_base_loop(main_base,flag);//進入監聽事件的循環,flag分爲阻塞和非阻塞
1)主線程根據用戶的設置創建n個worker線程,每個線程有一個待處理的連接句柄隊列conn_queue,且每個線程都使用libevent監聽一個管道並設置了處理函數thread_libevent_process
threads=(LIBEVENT_THREAD*)calloc(nthreads,sizeof(LIBEVENT_THREAD)); //LIBEVENT_THREAD,加入libevent元素的thread結構 “數組”
if(!threads)
{
perror("can't allocate thread des");
exit(1);
}
for(i=0;i<nthreads;i++) //設置thread和thread中的libevent所需屬性
{
int fds[2];
if(pipe(fds)) //thread和主線程的通信pipe
{
perror("can't create notify pipe");
exit(1);
}
threads[i].notify_receive_fd=fds[0];
threads[i].notify_send_fd=fds[1];
setup_event_thread(&threads[i]); //設置thread和thread中的libevent所需屬性
printf("init thread:%d\n",i);
}
for(i=0;i<nthreads;i++)
{
create_worker(worker_libevent,&threads[i]); //啓動thread
}
2)主線程首先使用libevent監聽一個主端口。當有連接事件到達時,accept獲得一個連接句柄FD,把該FD封裝到一個CQ_ITEM,然後選擇一個空閒的Worker線程,將CQ_ITEM壓入Worker線程的待處理的連接句柄隊列中,並往Worker線程家庭的管道中寫入一個"c"字符。
CQ_ITEM *item=cqi_new();
int tid=(last_thread+1)%settings.num_threads; //輪詢選出workerThread(數組)
LIBEVENT_THREAD *thread=workerThreads->threads+tid;
last_thread=tid;
item->sfd=sfd; //封裝必要的信息到item結構,後面會利用item封裝爲conn
item->init_state=init_state;
item->event_flags=event_flags;
item->read_buffer_size=read_buffer_size;
item->transport=transport;
write(thread->notify_send_fd,"c",1);//主線程和workerThread的通信方式,寫入到notify_send_fd告訴輔助線程item準備好了,可以處理
last_thread++;
3) Worker線程監聽到了管道可讀事件,觸發thread_libevent_process處理函數。從待處理的連接句柄隊列中取出一個CQ_ITEM,然後用libevent監聽CQ_ITEM裏面的連接句柄的可讀事件,並設置事件處理函數event_handler
LIBEVENT_THREAD *me=(LIBEVENT_THREAD*)arg;
CQ_ITEM *item;
char buf[1];
if(read(fd,buf,1)!=1)
fprintf(stderr,"can't read from libevent pipe\n");
item=cq_pop(me->new_conn_queue);
if(NULL!=item)
{
conn *c= conn_new (item->sfd,item->init_state,item->event_flags,
item->read_buffer_size,item->transport,me->base);
if(NULL==c)
{
if( IS_UDP(item->transport))
{
fprintf(stderr,"can't listen for events on UDP\n");
exit(1);
}
else
{
fprintf(stderr,"can't listen for events on fd %d\n",item->sfd);
close(item->sfd);
}
}
else
{
c->thread=me;
}
cqi_free(item);
}
conn *conn_new(const int sfd,enum conn_states init_state,const int event_flags,
const int read_buffer_size,enum network_transport transport,
struct event_base *base)
{
c->sfd=sfd;
event_set(&c->event,sfd,event_flags,event_handler,(void*)c);
event_base_set(base,&c->event);
c->ev_flags=event_flags;
if(event_add(&c->event,0)==-1)
{
if( conn_add_to_freelist(c))
conn_free(c);
perror("event_add");
return NULL;
}
return c;
}