Linux C/C++服务器
reactor的原理与百万并发
五元组
五元组:sip,sport,dip,dport,proto 源IP,源端口,目的IP,目的端口,协议
tcp的端口最大支持65536个,客户端和服务端ip都是固定的,为了测试百万并发,我们使用65536个客户端的端口,使用100个服务端的端口,也就是说服务端将会有100个listenfd在监听
reactor模型
reactor模型在epoll基础上新增动态扩容数组,使得每个fd都有自己的缓冲区buffer,并且查找fd也比较快
#define ITEM_LENGTH 1024
/*epoll 存在共用一个数据缓冲区问题,为了让每一个fd都有自己独立的缓冲区
所以就有了reactor*/
struct sock_item{ //conn_item
int fd; //clientfd
char *rbuffer;
int rlength;
char *wbuffer;
int wlength;
int event;
//callback
void (*recv_cb)(int fd, char *buffer, int length);
void (*send_cb)(int fd, char *buffer, int length);
void (*accept_cb)(int fd, char *buffer, int length);
};
/*动态扩容数组
1.在有序数据存储查找,动态扩容数组的空间和时间复杂度优于红黑树,并且结构简单,实际工作中经常会使用
*/
struct eventblock{
struct sock_item *items;
struct eventblock *next;
};
struct reactor{
int epfd;
int blkcnt;
struct eventblock* evblk;
};
int reactor_resize(struct reactor *r){ //新增 eventblock 扩容
if(r == NULL) return -1;
struct eventblock *blk = r->evblk;
while(blk != NULL && blk->next != NULL){ //移动到最后
blk = blk->next;
}
struct sock_item *item = (struct sock_item*)malloc(ITEM_LENGTH * sizeof(struct sock_item)); //新增item数组
if(item == NULL) return -4;
memset(item, 0, ITEM_LENGTH * sizeof(struct sock_item));
printf("---blkcnt---: %d\n", r->blkcnt);
struct eventblock *block = (struct eventblock*)malloc(sizeof(eventblock)); //新增eventblock模块
if(block == NULL){
free(item);
return -5;
}
memset(block, 0, sizeof(eventblock));
block->items = item; //新增eventblock赋值
block->next = NULL;
if(blk == NULL){
r->evblk = block; //连接eventblock模块
} else {
blk->next = block;
}
r->blkcnt++;
return 0;
}
struct sock_item* reactor_lookup(struct reactor *r, int sockfd){
if(r == NULL || sockfd <= 0) return NULL;
int blkidx = sockfd / ITEM_LENGTH;
//printf("reactor_lookup --> %d\n", r->blkcnt);
while(blkidx >= r->blkcnt){ //超出边界
reactor_resize(r); //扩容
}
int i=0;
struct eventblock *blk = r->evblk;
while(i++ < blkidx && blk != NULL){
blk = blk->next;
}
return &blk->items[sockfd % ITEM_LENGTH];
}