您的位置:首页 > 运维架构 > Linux

基于Linux epoll模型的Simple

2016-08-16 14:49 316 查看
关于epoll的介绍以及与其它I/O模型的优缺点,各大网站论坛均有介绍,这里就不多说了;

本Simple使用了epoll的边缘出发模式(ET),支持多客户端连接;

首先定义自己的数据结构以及相关全局变量:

#define MAX_EVENTS 10
#define MAX_BUFFER_SIZE 1024
#define LISTEN_PORT 8000

struct global_data
{
int server_sock;
int server_epoll;

};

struct client_data
{
char *read_buf;
char *write_buf;
int read_len;
int read_off;
int write_len;
int write_off;
int fd;
struct sockaddr_in addr;
};

struct global_data g_gd;

client_data结构体用来存储和处理客户端上下文数据;
下面初始化一个新的客户端上下文结构:

struct client_data *alloc_client_data(int fd, struct sockaddr_in *addr = NULL)
{
struct client_data *cd = NULL;

cd = (struct client_data *)malloc(sizeof(struct client_data));
if(cd == NULL)
{
return NULL;
}
memset(cd, 0, sizeof(struct client_data));

cd->fd = fd;
cd->write_len = MAX_BUFFER_SIZE;
cd->write_off = 0;
cd->read_len = MAX_BUFFER_SIZE;
cd->read_off = 0;
cd->write_buf = (char *)malloc(cd->write_len + 1);
cd->read_buf = (char *)malloc(cd->read_len + 1);
assert(cd->write_buf);
assert(cd->read_buf);
if(addr) memcpy(&cd->addr, addr, sizeof(cd->addr));

return cd;
}
释放客户端上下文数据:
void free_client_data(struct client_data *cd)
{
if(cd == NULL) return ;

close(cd->fd);
if(cd->write_buf) free(cd->write_buf);
if(cd->read_buf) free(cd->read_buf);

free(cd);

return ;
}
设置socket为非阻塞模式:
int set_sock_non_block(int fd)
{
int flags, result;

flags = fcntl(fd, F_GETFL, 0);
if(flags == -1)
{
return -1;
}
flags |= O_NONBLOCK;
result = fcntl(fd, F_SETFL, flags);
if(result == -1)
{
return -1;
}

return 0;
}
处理客户端连接请求:
int accept_client()
{
int ret = -1;
int addrlen;
int client_sock;
struct sockaddr_in remote_addr;
struct epoll_event e_event;

do
{
while(1)
{
addrlen = sizeof(struct sockaddr_in);
client_sock = accept(g_gd.server_sock, (struct sockaddr *)&remote_addr, (socklen_t *)&addrlen);
if(client_sock == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
{
break;
}
else
{
printf("accept failed, error: %s \n", strerror(errno));
break;
}
}
else
{
set_sock_non_block(client_sock);

struct client_data *cd = alloc_client_data(client_sock, &remote_addr);
assert(cd);
e_event.events = EPOLLIN | EPOLLET;
e_event.data.ptr = cd;
if(epoll_ctl(g_gd.server_epoll, EPOLL_CTL_ADD, client_sock, &e_event) == -1)
{
printf("epoll_ctl client failed \n");
free_client_data(cd);
}
else
{
printf("accept client: %s:%d \n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
}
}
}

ret = 0;

}while(0);

return ret;
}
接收客户端数据:
int recv_data(struct client_data *client)
{
int ret = -1;
int len_recv = 0;
struct epoll_event e_event;

if(client == NULL) return -1;

do
{
while(1)
{
len_recv = recv(client->fd, client->read_buf, client->read_len, 0);
if(len_recv == -1)
{
if(errno == EAGAIN)
{
break;
}
else
{
free_client_data(client);
break;
}
}
else if(len_recv == 0)
{
printf("client closed connection!!! \n");
free_client_data(client);
break;
}
else
{
client->read_buf[len_recv] = '\0';
printf("[%s:%d] recv data: %s:%d \n",
inet_ntoa(client->addr.sin_addr),
ntohs(client->addr.sin_port),
client->read_buf,
len_recv);

client->read_off = len_recv;

// send response
strcpy(client->write_buf, "OK!");
client->write_off = strlen(client->write_buf);

e_event.events = EPOLLOUT | EPOLLET;
e_event.data.ptr = client;
epoll_ctl(g_gd.server_epoll, EPOLL_CTL_MOD, client->fd, &e_event);
}
}

ret = 0;
}while(0);

return ret;
}
发送数据给客户端:
int send_data(struct client_data *client)
{
int ret = -1;
int len_send = 0;
struct epoll_event e_event;

if(client == NULL) return -1;

do
{
len_send = send(client->fd, client->write_buf, client->write_off, 0);
if(len_send == -1)
{
if(errno != EAGAIN)
{
printf("send error %s \n", strerror(errno));
free_client_data(client);
}
}
else if(len_send == 0)
{
free_client_data(client);
}
else
{
e_event.events = EPOLLIN | EPOLLET;
e_event.data.ptr = client;
epoll_ctl(g_gd.server_epoll, EPOLL_CTL_MOD, client->fd, &e_event);
}

ret = 0;

}while(0);

return ret;
}
服务器主程:
void epoll_server()
{
int len_recv;
int len_send;
int len_sin;
int signal_count;
struct sockaddr_in local_addr;
struct epoll_event e_event;
struct epoll_event e_events[MAX_EVENTS];
char buff[MAX_BUFFER_SIZE];

memset(&g_gd, '\0', sizeof(struct global_data));
memset(buff, '\0', sizeof(buff));

do
{
// create server socket
g_gd.server_sock = socket(AF_INET, SOCK_STREAM, 0);
if(g_gd.server_sock < 0)
{
printf("create socket failed \n");
break;
}

// bind
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = INADDR_ANY;
local_addr.sin_port = htons(LISTEN_PORT);
if(bind(g_gd.server_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1 )
{
printf("bind socket failed \n");
break;
}

// set non-block
if(set_sock_non_block(g_gd.server_sock) == -1)
{
printf("set_sock_non_block failed \n");
break;
}

// listen
if(listen(g_gd.server_sock, SOMAXCONN) == -1)
{
printf("listen fialed \n");
break;
}

// create epoll handle
g_gd.server_epoll = epoll_create(MAX_EVENTS);
if(g_gd.server_epoll < 0)
{
printf("epoll create failed \n");
break;
}

// register server socket event
e_event.events = EPOLLIN | EPOLLET;
e_event.data.fd = g_gd.server_sock;
if(epoll_ctl(g_gd.server_epoll, EPOLL_CTL_ADD, g_gd.server_sock, &e_event) < 0)
{
printf("epoll_ctl failed \n");
break;
}

while(1)
{
printf("epoll wait... \n");
signal_count = epoll_wait(g_gd.server_epoll, e_events, MAX_EVENTS, -1);
if(signal_count == -1) { printf("epoll_wait failed \n"); break; }

printf("wait signals: %d \n", signal_count);
for(int i=0; i<signal_count; ++i)
{
if(e_events[i].events & EPOLLERR || e_events[i].events & EPOLLHUP)
{
printf("socket error occured \n");
continue;
}
else if(e_events[i].data.fd == g_gd.server_sock)
{
accept_client();
}
else if(e_events[i].events & EPOLLIN)
{
recv_data((struct client_data *)e_events[i].data.ptr);
}
else if(e_events[i].events & EPOLLOUT)
{
send_data((struct client_data *)e_events[i].data.ptr);
}
else
{
printf("Some error happend \n");
}
}
}

}while(0);

close(g_gd.server_sock);
close(g_gd.server_epoll);

return ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux epoll