用链表保存fd
这样就涉及到了,链表的扩容,插入,轮询
首先
1.fd的结构体
struct sock_item { // conn_item
int fd; // clientfd
char *rbuffer;
int rlength; //
char *wbuffer;
int wlength;
int event;
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);
};
2.reactor的保存
这里reactor 存储epoll,链表的头和链表的块数,也就是当前容量
struct reactor {
int epfd; //epoll
int blkcnt;
struct eventblock *evblk;
};
3.链表的扩容
找到最后面,然后创建一个新的块放到最后
// 扩容
int reactor_resize(struct reactor *r) { // new 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));
if (item == NULL) return -4;// 获取失败
memset(item, 0, ITEM_LENGTH * sizeof(struct sock_item));
printf("-------------\n");
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL) {// 创建空间失败
free(item);
return -5;
}
memset(block, 0, sizeof(struct eventblock));
block->items = item;
block->next = NULL;
if (blk == NULL) {
r->evblk = block;
} else {
blk->next = block;
}
r->blkcnt ++;
return 0;
}
4.链表的轮询
先确定是哪个块, 当前fd对块长度取余确定,确定后直接找到位置。
struct sock_item* reactor_lookup(struct reactor *r, int sockfd) {
if (r == NULL) return NULL;
//if (r->evblk == NULL) return NULL;
if (sockfd <= 0) return NULL;
printf("reactor_lookup --> %d\n", r->blkcnt); //64
int blkidx = sockfd / ITEM_LENGTH;
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];
}
5.main函数
5.1对reactor进行初始化
struct reactor *r = (struct reactor*)calloc(1, sizeof(struct reactor));
if (r == NULL) {
return -3;
}
//memset();
r->epfd = epoll_create(1);
struct epoll_event ev, events[EVENTS_LENGTH];
5.2socket初始化,创建监听
int init_server(short port) {
int listenfd = socket(AF_INET, SOCK_STREAM, 0); //
if (listenfd == -1) return -1;
// listenfd
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) {
return -2;
}
#if 1 // nonblock
int flag = fcntl(listenfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(listenfd, F_SETFL, flag);
#endif
listen(listenfd, 10);
return listenfd;
}
int sockfds[PORT_COUNT] = {0};
int i = 0;
for (i = 0;i < PORT_COUNT;i ++) {
sockfds[i] = init_server(9999 + i);
ev.events = EPOLLIN;
ev.data.fd = sockfds[i]; //
epoll_ctl(r->epfd, EPOLL_CTL_ADD, sockfds[i], &ev);
}
5.2epoll开始监听事件
int nready = epoll_wait(r->epfd, events, EVENTS_LENGTH, -1); // -1, ms
//printf("------- %d\n", nready);
int i = 0;
for (i = 0;i < nready;i ++) {
int clientfd = events[i].data.fd;
if (is_listenfd(sockfds, clientfd)) { // accept
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(clientfd, (struct sockaddr*)&client, &len);
if (connfd == -1) break;
if (connfd % 1000 == 999) {
printf("accept: %d\n", connfd);
}
5.3 将fd加入epoll处理
ev.events = EPOLLIN;// 读事件
ev.data.fd = connfd;
epoll_ctl(r->epfd, EPOLL_CTL_ADD, connfd, &ev);
5.4 读事件处理
else if (events[i].events & EPOLLIN) { //clientfd
//char rbuffer[BUFFER_LENGTH] = {0};
struct sock_item *item = reactor_lookup(r, clientfd);
char *rbuffer = item->rbuffer;
char *wbuffer = item->wbuffer;
int n = recv(clientfd, rbuffer, BUFFER_LENGTH, 0);
if (n > 0) {
//rbuffer[n] = '\0';
printf("recv: %s, n: %d\n", rbuffer, n);
memcpy(wbuffer, rbuffer, BUFFER_LENGTH);
// 将数据读取
ev.events = EPOLLOUT;
// 修改为写事件,因为 模式就是 写-听-读
ev.data.fd = clientfd;
epoll_ctl(r->epfd, EPOLL_CTL_MOD, clientfd, &ev);
}
else if (n == 0) { // 表示断开连接了,直接清空
free(rbuffer);
free(wbuffer);
item->fd = 0;
close(clientfd);
}
5.5 写事件处理
else if (events[i].events & EPOLLOUT) {
//先找到fd的结构体,获取要发送的信息,然后发送
struct sock_item *item = reactor_lookup(r, clientfd);
char *wbuffer = item->wbuffer;
int sent = send(clientfd, wbuffer, BUFFER_LENGTH, 0); //
printf("sent: %d\n", sent);
ev.events = EPOLLIN;
ev.data.fd = clientfd;
epoll_ctl(r->epfd, EPOLL_CTL_MOD, clientfd, &ev);
}