reactor模型
- 组成:非阻塞的io + io多路复用
- 特征:基于事件循环,以事件驱动或事件回调的方式来实现业务逻辑
- 理解:对io的处理转换为对事件的处理。
单reactor模型
- redis
单reactor模型 + 任务队列 + 线程池
- skynet
多reactor
多线程
- memcached
多进程
- nginx
单reactor程序实例
下面的程序注册了accept回调函数,recv回调函数和send回调函数。相应的接收连接事件,读事件,写事件触发时会调用相应的回调函数。服务端收到客户端的数据再发送给客户端。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#define MAX_BUFFER_SIZE 5
#define MAX_EPOLL_EVENTS 20
#define EPOLL_LT 0
#define EPOLL_ET 1
#define FD_BLOCK 0
#define FD_NONBLOCK 1
typedef void (*CALLBACK) (int epollfd, int fd, int events, void *arg);
#define BUFFERLEN 1024
struct sockitem {
int sockfd;
CALLBACK callback;
char recvbuffer[BUFFERLEN];
char sendbuffer[BUFFERLEN];
int rlength;
int slength;
};
int set_noblock(int fd);
void recv_callback(int epollfd, int fd, int events, void *arg);
void send_callback(int epollfd, int fd, int events, void *arg)
{
printf("开始发送数据...\n");
struct sockitem* si = (struct sockitem*)arg;
int len = send(fd, si->sendbuffer, si->slength, 0);
si->callback =recv_callback;
struct epoll_event ep_event;
ep_event.events = EPOLLIN | EPOLLET;
ep_event.data.ptr = si;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ep_event);
printf("发送数据结束...\n");
}
void recv_callback(int epollfd, int fd, int events, void *arg)
{
char buffer[MAX_BUFFER_SIZE];
int ret;
struct sockitem* si = (struct sockitem*)arg;
memset(si->recvbuffer, 0, BUFFERLEN);
int offset = 0;
printf("带循环的ET读取数据开始...\n");
int isclosefd = 0;
while(1) {
memset(buffer, 0 , MAX_BUFFER_SIZE);
ret = recv(fd, buffer, MAX_BUFFER_SIZE, 0);
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("循环读取完所有数据!!!\n");
break;
}
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
free(si);
close(fd);
isclosefd = 1;
break;
} else if (ret == 0) {
printf("客户端主动关闭请求!!!\n");
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
free(si);
close(fd);
isclosefd = 1;
break;
} else {
printf("收到消息:%s, 共%d个字节\n", buffer, ret);
memcpy(si->recvbuffer + offset, buffer, ret);
offset += ret;
}
}
if (isclosefd == 0) {
printf("发送接收到的数据:%s\n", si->recvbuffer);
si->rlength = offset;
memset(si->sendbuffer, 0, BUFFERLEN);
memcpy(si->sendbuffer, si->recvbuffer, si->rlength);
si->slength = si->rlength;
si->callback = send_callback;
struct epoll_event ep_event;
ep_event.events = EPOLLOUT | EPOLLET;
ep_event.data.ptr = si;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ep_event);
}
printf("带循环的ET处理结束!!!\n");
}
void accept_callback(int epollfd, int sockfd, int events, void *arg)
{
struct sockaddr_in client_addr;
socklen_t client_addrlen;
printf("================新一轮accept=================");
printf("accept()开始...\n");
printf("开始休眠3秒...\n");
sleep(3);
printf("休眠3秒结束!!!\n");
client_addrlen = sizeof(client_addr);
int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addrlen);
printf("connfd = %d\n", connfd);
//addfd_to_epoll(epollfd, connfd, epoll_type, block_type);
struct epoll_event ep_event;
ep_event.events = EPOLLIN | EPOLLET;
set_noblock(connfd);
struct sockitem *si = (struct sockitem *)malloc(sizeof(struct sockitem));
si->sockfd = connfd;
si->callback = recv_callback;
ep_event.data.ptr = si;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ep_event);
printf("accept()结束!!!\n");
}
int set_noblock(int fd)
{
int old_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
return old_flags;
}
void err_exit(char* msg)
{
perror(msg);
exit(1);
}
int create_socket(const char* ip, const int port)
{
struct sockaddr_in server_addr;
int sockfd, reuse = 1;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(PF_INET, ip, &server_addr.sin_addr) == -1) {
err_exit("inet_pton() error");
}
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
err_exit("socket() error");
}
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
err_exit("bind() error");
}
if (listen(sockfd, 5) == -1) {
err_exit("listen() error");
}
return sockfd;
}
int main(int argc, char* argv[])
{
if (argc < 3) {
fprintf(stderr, "usage:%s ip_address port_number\n", argv[0]);
exit(1);
}
int sockfd, epollfd, number;
sockfd = create_socket(argv[1], atoi(argv[2]));
struct epoll_event events[MAX_EPOLL_EVENTS];
if ((epollfd = epoll_create1(0)) == -1) {
err_exit("epoll_create1 error");
}
// sockfd: 非阻塞的LT模式
struct sockitem *si = (struct sockitem *)malloc(sizeof(struct sockitem));
si->sockfd = sockfd;
si->callback = accept_callback;
struct epoll_event ep_event;
ep_event.data.ptr = si;
ep_event.events = EPOLLIN;
set_noblock(sockfd);
epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ep_event);
printf("listenfd:%d\n", sockfd);
while (1) {
number = epoll_wait(epollfd, events, MAX_EPOLL_EVENTS, -1);
if (number == -1) {
err_exit("epoll_wait() error");
} else {
printf("number:%d\n", number);
for (int i = 0; i < number; i++) {
if (events[i].events & EPOLLIN) {
struct sockitem *si = (struct sockitem *)events[i].data.ptr;
printf("EPOLLIN si->sockfd:%d\n", si->sockfd);
si->callback(epollfd, si->sockfd, events[i].events, si);
}
if (events[i].events & EPOLLOUT) {
struct sockitem *si = (struct sockitem *)events[i].data.ptr;
printf("EPOLLOUT si->sockfd:%d\n", si->sockfd);
si->callback(epollfd, si->sockfd, events[i].events, si);
}
}
}
}
close(sockfd);
return 0;
}
运行结果如下:
服务端:
客户端: