服务器端程序:
/********************************************************************
epoll_creat 创建一个epoll对象(内核中搞了个文件系统用来处理事件),
一般epollfd = epoll_create()
int epoll_create(int size);
epoll_ctl(epoll_add/epoll_del的合体)往epoll对象中增加/删除某一个流的某一个事件
//epfd epoll句柄
//fd 套接字
//event
//op 增加 切换模式(水平触发和边沿触发(只支持no-block socket)模式)删除
//EPOLL_CTL_ADD
//EPOLL_CTL_MOD
//EPOLL_CTL_DEL
epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
//
typedef union epoll_data
{
void *ptr;
int fd;
_uint32_t u32;
_uint64_t u64;
}epoll_data_t;
struct epoll_event
{
_uint32_t events; //Epoll events
epoll_data_t data; //User data variable
}
_uint32_t events :
The events member is a bit mask composed using the following available event types:
EPOLLIN
The associated file is available for read(2) operations.
EPOLLOUT
The associated file is available for write(2) operations.
EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. (This flag is especially useful for writing simple code to detect peer
shutdown when using Edge Triggered monitoring.)
EPOLLPRI
There is urgent data available for read(2) operations.
EPOLLERR
Error condition happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events.
EPOLLHUP
Hang up happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events. Note that
EPOLLOUT
The associated file is available for write(2) operations.
EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. (This flag is especially useful for writing simple code to detect peer
shutdown when using Edge Triggered monitoring.)
EPOLLPRI
There is urgent data available for read(2) operations.
EPOLLERR
Error condition happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events.
EPOLLHUP
Hang up happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events. Note that
when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent
reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
EPOLLET
Sets the Edge Triggered behavior for the associated file descriptor. The default behavior for epoll is Level Triggered. See epoll(7) for more detailed
information about Edge and Level Triggered event distribution architectures.
EPOLLONESHOT (since Linux 2.6.2)
Sets the one-shot behavior for the associated file descriptor. This means that after an event is pulled out with epoll_wait(2) the associated file
descriptor is internally disabled and no other events will be reported by the epoll interface. The user must call epoll_ctl() with EPOLL_CTL_MOD to
rearm the file descriptor with a new event mask.
EPOLLWAKEUP (since Linux 3.5)
If EPOLLONESHOT and EPOLLET are clear and the process has the CAP_BLOCK_SUSPEND capability, ensure that the system does not enter "suspend" or "hiber‐
nate" while this event is pending or being processed. The event is considered as being "processed" from the time when it is returned by a call to
epoll_wait(2) until the next call to epoll_wait(2) on the same epoll(7) file descriptor, the closure of that file descriptor, the removal of the event
file descriptor with EPOLL_CTL_DEL, or the clearing of EPOLLWAKEUP for the event file descriptor with EPOLL_CTL_MOD. See also BUGS.
epoll_wait(epollfd,...)等待直到注册的事件发生
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
while(TRUE)
{
fds = epoll_wait();
for(i=0; i < fds;i++)
{
//do something
}
}
********************************************************************/
/********************************************************************
并发编程方法:
1、ache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型
2、select模型和poll模型
3、epoll模型
********************************************************************/
#include"mylib.h"
#include<sys/epoll.h>
int ipv4_tcp_creat_socket(void)
{
int opt;
socklen_t len;
int listenfd;
struct sockaddr_in server;
if((listenfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("Creat socket failed\n");
return -1;
}
//监听套接字,地址可重用
if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0)
{
perror("Error,set socket reuse addr failed\n");
return -2;
}
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(SERV_PROT);
//server.sin_addr.s_addr = inet_addr("127.0.0.1");
//inet_pton(AF_INET,"127.0.0.1",&server.sin_addr);
server.sin_addr.s_addr = htonl(INADDR_ANY);
len = sizeof(struct sockaddr);
if(bind(listenfd,(struct sockaddr*)(&server),len)<0)
{
perror("bind error\n");
return -3;
}
listen(listenfd,MAX_LISTEN_QUE);
return listenfd;
}
int process_data(int sockfd)
{
int bytes;
char buf[MAX_BUFFER_SIZE];
char *s = buf;
char flag = 1;
int len = 0;
memset(buf,0,MAX_BUFFER_SIZE);
while(flag)
{
bytes = recv(sockfd,s,ReadLine,0);
//如果是 ReadLine 个字节,继续去读
if(bytes == ReadLine)
{
flag = 1;
}
//如果不是五个字节,就退出
else
{
flag = 0;
}
if(bytes<0)
{
//缓冲区没数据了
if(errno == EAGAIN)
{
printf("no data\n");
break;
}
perror("read err\n");
return -1;
}
if(bytes == 0)
{
printf("client connect closed\n");
//close(sockfd);
return -2;
}
//记录他读了多长数据
s += bytes;
len += bytes;
}
printf("buf:%s,len:%d\n",buf,len);
send(sockfd,buf,len,0);
//memset(buf,0,MAX_BUFFER_SIZE);
return 0;
}
int main(int argc,char *argv[])
{
int epollfd,sockfd;
int listenfd;
struct epoll_event ev,events[MAX_EVENTS];
struct sockaddr_in client;
int len;
int i,rv,fds;
len = sizeof(struct sockaddr_in);
epollfd = epoll_create(MAX_EVENTS);
if(epollfd < 0)
{
perror("epoll_create err:");
return -1;
}
//监听套接字
listenfd = ipv4_tcp_creat_socket();
//将监听套接字 listenfd 变成非阻塞模式
fcntl(listenfd,F_SETFL,O_NONBLOCK);
ev.data.fd = listenfd;
ev.events = EPOLLIN;
rv = epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&ev);
if(rv < 0)
{
perror("epoll_ctl err:");
return -1;
}
while(1)
{
//-1:永远等待
fds = epoll_wait(epollfd,events,MAX_EVENTS,-1);
if(fds < 0)
{
perror("epoll_wait err:");
return -1;
}
for(i = 0;i < fds; i++)
{
//连接事件
if(events[i].data.fd == listenfd)
{
sockfd = accept(listenfd,(struct sockaddr *)&client,&len);
if(sockfd < 0)
{
perror("accept err:");
continue;
}
printf("sockfd = %d\n",sockfd);
printf("IP:%s,Port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
ev.data.fd = sockfd;
//EPOLLET 边缘触发模式
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev);
continue;
}
else
{
rv = process_data(events[i].data.fd);
if(rv == -2)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,&ev);
close(events[i].data.fd);
continue;
}
else
{
}
}
}
}
return 0;
}
实验结果: