下面是百万并发reactor网络模型的代码实现,实测连接的并发量可达100W以上,可以基于此模型构建对应的应用。
很多朋友在网络编程时,在对代码测试时,分不清并发量和吞吐量的关系。
并发量是指系统在同一时间内能够处理的请求数量,它主要关注的是系统能够同时处理多少个请求。而吞吐量则是指在一定时间内,系统能够处理的请求总数,它更关注的是系统整体的处理能力。
我所理解的reactor:
把对网络IO的处理装换为对事件的处理,进行封装。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/sendfile.h>
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 8888
#define PORT_COUNT 1
// 这是一个C语言中的函数指针类型定义。
// NCALLBACK是一个函数指针类型,它指向一个接受三个参数的函数:
// 两个int类型的参数和一个void指针类型的参数,并返回一个int类型的值。
typedef int NCALLBACK(int, int, void *);
// 把每个请求封装为一个event
struct ntyevent
{
int fd;
int events;
void *arg;
int (*callback)(int fd, int events, void *arg);
int status;
char buffer[BUFFER_LENGTH];
char wbuffer[BUFFER_LENGTH];
int length;
int wlength;
};
// 封装一个events块de链表
struct eventblock
{
struct ntyevent *events;
struct eventblock *next;
};
// 封装reactor
struct ntyreactor
{
int epfd;
int blkcnt;
struct eventblock *evblks;
};
int ntyreactor_destory(struct ntyreactor *reactor);
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd);
int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);
void nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg)
{
ev->fd = fd;
ev->callback = callback;
ev->events = 0;
ev->arg = arg;
return;
}
int nty_event_add(int epfd, int events, struct ntyevent *ev)
{
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
if (ev->status == 1)
{
op = EPOLL_CTL_MOD;
}
else
{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0)
{
printf("event add failed [fd=%d], events[%d]\n", ev->fd, events);
return -1;
}
return 0;
}
int nty_event_del(int epfd, struct ntyevent *ev)
{
struct epoll_event ep_ev = {0, {0}};
if (ev->status != 1)
{
return -1;
}
ep_ev.data.ptr = ev;
ev->status = 0;
epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev);
return 0;
}
int curfds = 0;
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
struct timeval tv_begin;
int accept_cb(int fd, int events, void *arg)
{
struct ntyreactor *reactor = (struct ntyreactor *)arg;
if (reactor == NULL)
return -1;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int clientfd;
if ((clientfd = accept(fd, (struct sockaddr *)&client_addr, &len)) == -1)
{
if (errno != EAGAIN && errno != EINTR)
{
}
printf("accept: %s\n", strerror(errno));
return -1;
}
int flag = 0;
if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0)
{
printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
return -1;
}
struct ntyevent *event = ntyreactor_idx(reactor, clientfd);
if (event == NULL)
return -1;
nty_event_set(event, clientfd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
if (curfds++ % 1000 == 999)
{
struct timeval tv_cur;
memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));
gettimeofday(&tv_begin, NULL);
int time_used = TIME_SUB_MS(tv_begin, tv_cur);
printf("connections: %d, sockfd:%d, time_used:%d\n", curfds, clientfd, time_used);
}
// printf("new connect [%s:%d], pos[%d]\n",
// inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);
return 0;
}
int recv_cb(int fd, int events, void *arg)
{
struct ntyreactor *reactor = (struct ntyreactor *)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL)
{
return -1;
}
int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);
nty_event_del(reactor->epfd, ev);
if (len > 0)
{
ev->length = len;
ev->buffer[len] = '\0';
printf("recv [%d]:%s\n", fd, ev->buffer);
nty_event_set(ev, fd, send_cb, reactor);
nty_event_add(reactor->epfd, EPOLLOUT, ev);
}
else if (len == 0)
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
else
{
if (errno == EAGAIN && errno == EWOULDBLOCK)
{ //
}
else if (errno == ECONNRESET)
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
}
return len;
}
int send_cb(int fd, int events, void *arg)
{
struct ntyreactor *reactor = (struct ntyreactor *)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL)
return -1;
int len = send(fd, ev->buffer, ev->length, 0);
if (len > 0)
{
nty_event_del(reactor->epfd, ev);
nty_event_set(ev, fd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, ev);
}
else
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
return len;
}
int ntyreactor_init(struct ntyreactor *reactor)
{
if (reactor == NULL)
return -1;
memset(reactor, 0, sizeof(struct ntyreactor));
reactor->epfd = epoll_create(1);
if (reactor->epfd <= 0)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
return -2;
}
struct ntyevent *evs = (struct ntyevent *)malloc(MAX_EPOLL_EVENTS * sizeof(struct ntyevent));
if (evs == NULL)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epfd);
return -3;
}
memset(evs, 0, MAX_EPOLL_EVENTS * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL)
{
free(evs);
close(reactor->epfd);
return -4;
}
block->events = evs;
block->next = NULL;
reactor->blkcnt = 1;
reactor->evblks = block;
return 0;
}
int init_sock(short port)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (listen(fd, 20) < 0)
{
printf("listen failed : %s\n", strerror(errno));
return -1;
}
printf("server listen on port: %d\n", port);
gettimeofday(&tv_begin, NULL);
return fd;
}
int ntyreactor_alloc(struct ntyreactor *reactor)
{
if (reactor == NULL)
return -1;
if (reactor->evblks == NULL)
return -1;
struct eventblock *blk = reactor->evblks;
while (blk->next != NULL)
{
blk = blk->next;
}
struct ntyevent *evs = (struct ntyevent *)malloc(MAX_EPOLL_EVENTS * sizeof(struct ntyevent));
if (evs == NULL)
{
printf("ntyreactor_alloc ntyevent failed\n");
return -2;
}
memset(evs, 0, MAX_EPOLL_EVENTS * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL)
{
printf("ntyreactor_alloc eventblock failed\n");
return -3;
}
block->events = evs;
block->next = NULL;
blk->next = block;
reactor->blkcnt++;
return 0;
}
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd)
{
if (reactor == NULL)
return NULL;
if (reactor->evblks == NULL)
return NULL;
int blkidx = sockfd / MAX_EPOLL_EVENTS;
while (blkidx >= reactor->blkcnt)
{
ntyreactor_alloc(reactor);
}
int i = 0;
struct eventblock *blk = reactor->evblks;
while (i++ != blkidx && blk != NULL)
{
blk = blk->next;
}
return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}
int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor)
{
if (reactor == NULL)
return -1;
if (reactor->evblks == NULL)
return -1;
struct ntyevent *event = ntyreactor_idx(reactor, sockfd);
if (event == NULL)
return -1;
nty_event_set(event, sockfd, acceptor, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
return 0;
}
int ntyreactor_run(struct ntyreactor *reactor)
{
if (reactor == NULL)
return -1;
if (reactor->epfd < 0)
return -1;
if (reactor->evblks == NULL)
return -1;
struct epoll_event events[MAX_EPOLL_EVENTS + 1];
int checkpos = 0;
int i;
while (1)
{
int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
if (nready < 0)
{
printf("epoll_wait error, exit\n");
continue;
}
for (i = 0; i < nready; i++)
{
struct ntyevent *ev = (struct ntyevent *)events[i].data.ptr;
if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN))
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT))
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
}
}
}
int ntyreactor_destory(struct ntyreactor *reactor)
{
close(reactor->epfd);
struct eventblock *blk = reactor->evblks;
struct eventblock *blk_next;
while (blk != NULL)
{
blk_next = blk->next;
free(blk->events);
free(blk);
blk = blk_next;
}
return 0;
}
int main(int argc, char *argv[])
{
struct ntyreactor *reactor = (struct ntyreactor *)malloc(sizeof(struct ntyreactor));
ntyreactor_init(reactor);
unsigned short port = SERVER_PORT;
if (argc == 2)
{
port = atoi(argv[1]);
}
int i = 0;
int sockfds[PORT_COUNT] = {0};
for (int i = 0; i < PORT_COUNT; i++)
{
sockfds[i] = init_sock(port + i);
ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
}
ntyreactor_run(reactor);
ntyreactor_destory(reactor);
for (i = 0; i < PORT_COUNT; i++)
{
close(sockfds[i]);
}
free(reactor);
return 0;
}