关于epoll的相关知识我不就写了,只把我弄的例子写下,我整的这个epoll服务器其实就只是把man epoll中举得那个例子给做完善了。
主要参考了man epoll和百度百科中关于epoll的介绍来实现的。
服务器代码:
/*************************************************
Author: xiongchuanliang
Description: 基于epoll的服务器,就是把man epoll中的例子补充完整了。
编译命令:
Linux:
g++ -o tcpepoll2 tcpepoll2.cpp -m64 -I./common
**************************************************/
// 服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "initsock.h"
#include "common.h"
#include <sys/epoll.h>
CInitSock initSock;
#define MAX_EVENTS 10
int do_use_fd(int client); //与客户端交互
int setnonblocking(int sock); // 设置套接字为不阻塞
int main(int argc, char* argv[])
{
int n = 0;
//创建套接字
SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen == INVALID_SOCKET)
{
PrintError("socket() failed.\n");
exit(EXIT_FAILURE);
}
//绑定本地IP和端口到套接字
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVPORT); //大于1024且小于65535
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero),8);
//SO_REUSEADDR : 使bind函数能允许地址立即重用
int on = 1;
setsockopt( sListen, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on) );
if(bind(sListen,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == SOCKET_ERROR)
{
PrintError("bind() failed.");
exit(EXIT_FAILURE);
}
//开始监听
if(listen(sListen, BACKLOG) == SOCKET_ERROR)
{
PrintError("sListen() failed.");
exit(EXIT_FAILURE);
}
struct epoll_event ev,events[MAX_EVENTS];
SOCKET conn_sock, nfds, epollfd; //listen_sock
//指定内核需要监听的描述数
//函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epollfd = epoll_create(10);
if (epollfd == -1) {
PrintError("epoll_create");
exit(EXIT_FAILURE);
}
/*
EPOLLLT 默认行为,只要一个文件描述符处于就绪状态,epoll 就会不停的通知你有事件发生。传统的 select/poll 都是这样处理。
EPOLLET 属新方式,只在一个文件描述符新处于就绪的时候通知一次,之后不管数据有没有读完,都不会再通知,当然,有新数据到还是会通知的。
所以,用 EPOLLET 的时候,一定要把文件描述符设置为 non-blocking,
而且最好是一直读数据,读到返回 EAGAIN 才停下
*/
//指定内核需要监听的事件,并注册一个新的fd到epoll_create()返回的epollfd中
ev.events = EPOLLIN | EPOLLET; //对读感兴趣,边沿触发模式
ev.data.fd = sListen;
//函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sListen, &ev) == -1) {
PrintError("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
socklen_t nAddrlen = sizeof(struct sockaddr_in);
for (;;) {
//函数原型: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
//MAX_EVENTS的值不能大于 epoll_create()指定的值
//等待epoll_ctl指定的监听的事件发生
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
<span style="white-space:pre"> </span>continue; // PrintError("epoll_pwait()");
}
//遍历发生了指定监听的事件事件的fd
for (n = 0; n < nfds; ++n) {
// 如果是主socket的事件的话,则表示
// 有新连接进入了,进行新连接的处理。
if (events[n].data.fd == sListen) { //连接指定的客户端
// conn_sock = accept(sListen,(struct sockaddr *) &local, &addrlen);
conn_sock = accept(sListen,(struct sockaddr *) &server_addr, &nAddrlen);
if (conn_sock == -1) {
PrintError("accept()");
continue;
}
// 将新连接设置为非阻塞模式
setnonblocking(conn_sock);
//设置event为监控读操作,即客户端有发送数据过来。
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
//将新设置的这个event加入到epoll的监听队列里
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
PrintError("epoll_ctl: conn_sock");
}
}else{
do_use_fd(events[n].data.fd);
}
} //end for (n = 0; n < nfds; ++n)
} //end for (;;)
//关闭监听套接字
close(sListen);
//关闭epoll句柄
close(epollfd);
exit(EXIT_SUCCESS);
}
//与客户端交互
int do_use_fd(int client)
{
char recvData[MAXDATASIZE]={0};
int recvbytes = read(client,recvData, MAXDATASIZE);
if( recvbytes == 0)
{
printf("read() no data!\n");
}else if( recvbytes < 0)
{
if (errno != EAGAIN)
{
PrintError("read() failed.");
close (client);
}
}else if( recvbytes > 0)
{
recvData[recvbytes]='\0';
printf("msg:%s\n",recvData);
char sendData[MAXDATASIZE]={0};
strcpy(sendData,"Hello client!\n");
int sendbytes = write(client, sendData, sizeof(sendData)) ;
if( sendbytes < 0)
{
PrintError("write() failed.");
close (client);
}else{
printf("write() success! %d \n",sendbytes);
}
}
}
// 设置套接字为不阻塞
int setnonblocking(int sock)
{
int flags = fcntl (sock, F_GETFL, 0);
if (flags == -1)
{
PrintError("fcntl() failed.");
return -1;
}
flags |= O_NONBLOCK;
if ( (fcntl (sock, F_SETFL, flags)) == -1)
{
PrintError("fcntl() failed. O_NONBLOCK");
return -1;
}
return 0;
}
代码中用到的头文件在<< 网络编程(1)跨平台的Socket同步阻塞工作模式例子>>
MAIL: xcl_168@aliyun.com
BLOG: http://blog.youkuaiyun.com/xcl168