本文转自:http://blog.csdn.net/xiaofei_hah0000/article/details/8743627
阻塞模式下服务器的并发只有几K,而真正的server 像nginx, apache, yumeiz 轻轻松松处理几万个并发完全不在话下,因此大并发的场合下是不能用阻塞的。
1W的并发是一个分隔点,如果单进程模型下能达到 的话,说明至少在服务器这块你已经很厉害了。
服务器开发就像一门气功,能不能搞出大并发,容错性处理得怎么样,就是你有没有内功,内功有多深。
异步模式是专门为大并发而生,linux下一般用 epoll 来管理事件,下面就开始我们的异步大并发服务器实战吧。
跟阻塞开发一样,先来看看设计过程:
1.创建事件模型。
2.创建监听连接并监听。
3.将监听连接加入事件模型。
4.当有事件时,判断事件类型。
5.若事件为监听连接,则产生客户连接同时加入事件模型,对客户连接接收发送。
6.若事件为客户连接,处理相应IO请求。
为了让大家一概全貌,我用一个函数实现的( 一个函数写一个2W并发的服务器,你试过么),可读性可能会差点,但是对付这道面试题是绰绰有余了。
实际开发过程如下:
先定义一个事件结构,用于对客户连接进行缓存
- struct my_event_s {
- int fd;
- char recv[64];
- char send[64];
- int rc_pos;
- int sd_pos;
- };
建立缓存对象:
- struct epoll_event wait_events[EPOLL_MAX];
- struct my_event_s my_event[EPOLL_MAX];
创建监听连接:
- sock_server = socket(AF_INET, SOCK_STREAM, 0);
- flag = fcntl(sock_server, F_GETFL, 0);
- fcntl(sock_server, F_SETFL, flag | O_NONBLOCK); /* 设定为监听套接口为非阻塞 */
绑定地址并监听:
- flag = bind(sock_server, (struct sockaddr *)&addr_server, sizeof(struct sockaddr));
- if (flag < 0) {
- printf("your bind is not ok\n");
- close(sock_server);
- return 0;
- }
- flag = listen(sock_server, 1024);
- if (flag < 0) {
- printf("your listen is not ok\n");
- close(sock_server);
- return 0;
- }
- epfd = epoll_create(EPOLL_MAX);
- if (epfd <= 0) {
- printf("event module could not be setup\n");
- close(sock_server);
- return 0;
- }
- tobe_event.events = EPOLLIN;
- tobe_event.data.fd = sock_server;
- epoll_ctl(epfd, EPOLL_CTL_ADD, sock_server, &tobe_event);
事件模型处理:
- e_num = epoll_wait(epfd, wait_events, EPOLL_MAX, WAIT_TIME_OUT);
- if (e_num <= 0) {
- continue;
- }
- for (i = 0; i < e_num; ++i) {
- if (sock_server == wait_events[i].data.fd) {
- while (1) {
连接客户端:
- sock_client = accept(sock_server, (struct sockaddr *)&addr_client, (socklen_t *)&size);
- if (sock_client < 0) {
- if (errno == EAGAIN) {
- break;
- }
- if (errno == EINTR) {
- continue;
- }
- break;
- }
- flag = fcntl(sock_client, F_GETFL, 0);
- fcntl(sock_client, F_SETFL, flag | O_NONBLOCK);
- tobe_event.events = EPOLLIN | EPOLLET;
- tobe_event.data.u32 = my_empty_index;
- epoll_ctl(epfd, EPOLL_CTL_ADD, sock_client, &tobe_event);
- ++num;
- current = time(0);
- if (current > last) {
- printf("last sec qps:%d\n", num);
- num = 0;
- last = current;
- }
- memcpy(tobe_myevent->send, ¤t, sizeof(time_t));
接收连接内容:
- flag = recv(sock_client, tobe_myevent->recv, 64, 0);
- if (flag < 64) {
- if (flag > 0) {
- tobe_myevent->rc_pos += flag;
- }
- continue;
- }
- if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
- printf("your recv does follow the protocal\n");
- tobe_myevent->fd = 0;
- close(sock_client);
- continue;
- }
-
- flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
- if (flag < sizeof(time_t)) {
- <span style="white-space:pre"> </span>tobe_event.events = EPOLLET | EPOLLOUT;
- <span style="white-space:pre"> </span>epoll_ctl( epfd, EPOLL_CTL_MOD, sock_client, &tobe_event );
- if( flag > 0 )
- tobe_myevent->sd_pos += flag;
- continue;
- }
- tobe_myevent->fd = 0;
- close( sock_client );
后面进行普通连接事件处理,错误处理:
- if (event_flag | EPOLLHUP) {
- tobe_myevent->fd = 0;
- close( sock_client );
- continue;
- } else if (event_flag | EPOLLERR) {
- tobe_myevent->fd = 0;
- close( sock_client );
- continue;
- }
- else if (event_flag | EPOLLOUT) {
- if (tobe_myevent->rc_pos != 64) {
- continue;
- }
- if (tobe_myevent->sd_pos >= sizeof(time_t)) {
- tobe_myevent->fd = 0;
- close( sock_client );
- continue;
- }
- flag = send(sock_client, tobe_myevent->send
- + tobe_myevent->sd_pos, sizeof(time_t) - tobe_myevent->sd_pos, 0);
- if (flag < 0) {
- if (errno == EAGAIN) {
- continue;
- } else if (errno == EINTR) {
- continue;
- }
- tobe_myevent->fd = 0;
- close(sock_client);
- continue;
- }
- if (flag > 0) {
- tobe_myevent->sd_pos += flag;
- if (tobe_myevent->sd_pos >= sizeof(time_t)) {
- tobe_myevent->fd = 0;
- close(sock_client);
- continue;
- }
- }
- }
- if (event_flag | EPOLLIN) {
- if (tobe_myevent->rc_pos < 64) {
- flag = recv(sock_client, tobe_myevent->recv
- + tobe_myevent->rc_pos, 64 - tobe_myevent->rc_pos, 0);
- if (flag <= 0) {
- continue;
- }
-
- tobe_myevent->rc_pos += flag;
- if (tobe_myevent->rc_pos < 64) {
- continue;
- }
- if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
- printf( "your recv does follow the protocal\n");
- tobe_myevent->fd = 0;
- close(sock_client);
- continue;
- }
- flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
- if (flag < sizeof(time_t)) {
- if (flag > 0)
- tobe_myevent->sd_pos += flag;
- <span style="white-space:pre"> </span>tobe_event.events = EPOLLET | EPOLLOUT;
- tobe_event.data.u32 = wait_events[i].data.u32;
- epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);
- continue;
- }
- tobe_myevent->fd = 0;
- close(sock_client);
- }
- }
到此,一个异步服务器搭建完毕,轻松实现2W的并发量,代码你可以在这里下载,下一篇是完整的代码,满足喜欢在网上看代码的码狂们。。。
完整代码如下:
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
struct my_event_s {
int fd;
char recv[64];
char send[64];
int rc_pos;
int sd_pos;
};
int main(int argc, char *argv[]) {
int port;
int flag;
int size;
int sock_server;
int sock_client;
time_t current;
time_t last;
int num;
int e_num;
int my_empty_index;
int i,j;
int event_flag;
#define EPOLL_MAX 51200
struct epoll_event wait_events[EPOLL_MAX];
struct my_event_s my_event[EPOLL_MAX];
struct my_event_s* tobe_myevent;
struct epoll_event tobe_event;
int epfd;
#define RECV_BUF_LEN 256
char buffer[RECV_BUF_LEN];
struct sockaddr_in addr_server;
struct sockaddr_in addr_client;
if (argc <= 1) {
printf("please set your port\n");
return 0;
}
printf("your port:%s\n", argv[1]);
port = atoi(argv[1]);
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(port);
addr_server.sin_addr.s_addr = htonl(INADDR_ANY);
sock_server = socket(AF_INET, SOCK_STREAM, 0);
flag = fcntl(sock_server, F_GETFL, 0);
fcntl(sock_server, F_SETFL, flag | O_NONBLOCK);
flag = bind(sock_server, (struct sockaddr *)&addr_server, sizeof(struct sockaddr));
if (flag < 0) {
printf("your bind is not ok\n");
close(sock_server);
return 0;
}
flag = listen(sock_server, 1024);
if (flag < 0) {
printf("your listen is not ok\n");
close(sock_server);
return 0;
}
epfd = epoll_create(EPOLL_MAX);
if (epfd <= 0) {
printf("event module could not be setup\n");
close(sock_server);
return 0;
}
tobe_event.events = EPOLLIN;
tobe_event.data.fd = sock_server;
epoll_ctl(epfd, EPOLL_CTL_ADD, sock_server, &tobe_event);
size = sizeof(addr_client);
num = 0;
last = 0;
my_empty_index = 0;
while (1) {
#define WAIT_TIME_OUT 600
e_num = epoll_wait(epfd, wait_events, EPOLL_MAX, WAIT_TIME_OUT);
if (e_num <= 0) {
continue;
}
for (i = 0; i < e_num; ++i) {
if (sock_server == wait_events[i].data.fd) {
while (1) {
sock_client = accept(sock_server, (struct sockaddr *)&addr_client, (socklen_t *)&size);
if (sock_client < 0) {
if (errno == EAGAIN) {
break;
}
if (errno == EINTR) {
continue;
}
break;
}
tobe_myevent = my_event + my_empty_index;
memset(tobe_myevent, 0, sizeof(struct my_event_s));
tobe_myevent->fd = sock_client;
flag = fcntl(sock_client, F_GETFL, 0);
fcntl(sock_client, F_SETFL, flag | O_NONBLOCK);
tobe_event.events = EPOLLIN | EPOLLET;
tobe_event.data.u32 = my_empty_index;
epoll_ctl(epfd, EPOLL_CTL_ADD, sock_client, &tobe_event);
for(j = my_empty_index + 1; j < EPOLL_MAX; ++j) {
if (!my_event[j].fd) {
my_empty_index = j;
break;
}
}
if (my_event[j].fd) {
for (j = 0; j < EPOLL_MAX; ++j) {
if (!my_event[j].fd) {
my_empty_index = j;
break;
}
}
if (my_event[j].fd) {
printf("your events has been none else\n");
close(sock_client);
close(sock_server);
return 0;
}
}
++num;
current = time(0);
if (current > last) {
printf("last sec qps:%d\n", num);
num = 0;
last = current;
}
memcpy(tobe_myevent->send, ¤t, sizeof(time_t));
flag = recv(sock_client, tobe_myevent->recv, 64, 0 );
if (flag < 64) {
if (flag > 0)
tobe_myevent->rc_pos += flag;
continue;
}
if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
printf("your recv does follow the protocal\n");
tobe_myevent->fd = 0;
close(sock_client);
continue;
}
flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
if (flag < sizeof(time_t)) {
tobe_event.events = EPOLLET | EPOLLOUT;
epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);
if (flag > 0)
tobe_myevent->sd_pos += flag;
continue;
}
tobe_myevent->fd = 0;
close(sock_client);
}
} else {
tobe_myevent = my_event + wait_events[i].data.u32;
sock_client = tobe_myevent->fd;
event_flag = wait_events[i].events;
if (event_flag | EPOLLHUP) {
tobe_myevent->fd = 0;
close(sock_client);
continue;
} else if( event_flag | EPOLLERR) {
tobe_myevent->fd = 0;
close(sock_client);
continue;
} else if (event_flag | EPOLLOUT) {
if (tobe_myevent->rc_pos != 64) {
continue;
}
if (tobe_myevent->sd_pos >= sizeof(time_t)) {
tobe_myevent->fd = 0;
close(sock_client);
continue;
}
flag = send(sock_client, tobe_myevent->send + tobe_myevent->sd_pos, sizeof(time_t) - tobe_myevent->sd_pos, 0);
if (flag < 0) {
if (errno == EAGAIN) {
continue;
} else if( errno == EINTR) {
continue;
}
tobe_myevent->fd = 0;
close(sock_client);
continue;
}
if (flag > 0) {
tobe_myevent->sd_pos += flag;
if (tobe_myevent->sd_pos >= sizeof(time_t)) {
tobe_myevent->fd = 0;
close(sock_client);
continue;
}
}
}
if (event_flag | EPOLLIN) {
if (tobe_myevent->rc_pos < 64) {
flag = recv(sock_client, tobe_myevent->recv + tobe_myevent->rc_pos, 64 - tobe_myevent->rc_pos, 0);
if (flag <= 0) {
continue;
}
tobe_myevent->rc_pos += flag;
if (tobe_myevent->rc_pos < 64) {
continue;
}
if (tobe_myevent->recv[31] || tobe_myevent->recv[63]) {
printf("your recv does follow the protocal\n");
tobe_myevent->fd = 0;
close(sock_client);
continue;
}
flag = send(sock_client, tobe_myevent->send, sizeof(time_t), 0);
if (flag < sizeof(time_t)) {
if (flag > 0)
tobe_myevent->sd_pos += flag;
tobe_event.events = EPOLLET | EPOLLOUT;
tobe_event.data.u32 = wait_events[i].data.u32;
epoll_ctl(epfd, EPOLL_CTL_MOD, sock_client, &tobe_event);
continue;
}
tobe_myevent->fd = 0;
close(sock_client);
}
}
}
}
}
printf("close server connection\n");
close(sock_server);
return 0;
}