水平触发:当被监控的文件描述符上有可读写的时间发生时,epoll_wait会通知处理程序去读写,如果你没有读写完,下次epoll_wait就会再直通知你。
垂直触发:当被监控的文件描述符上有可读写时间发生时,epoll_wait会通知处理程序去读写,如果你没有读写玩,下次epoll_wait就不会在通知你。
直接看代码,源地址代码我略微改了一下:
代码地址:http://blog.csdn.net/stpeace/article/details/73692958
水平触发:ev.events = EPOLLIN 垂直触发: ev.events = EPOLLIN|EPOLLET;//垂直触发
服务端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#define MAXLINE 4096
#define BACKLOG 100
int main()
{
int iListenSock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
inet_aton("0.0.0.0", &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
int iOpt = 1;
setsockopt(iListenSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt)); // 标配
bind(iListenSock, (sockaddr*)&addr, sizeof(addr));
listen(iListenSock, BACKLOG);
epoll_event ev;
ev.data.fd = iListenSock;
//ev.events = EPOLLIN|EPOLLET;//垂直触发
ev.events = EPOLLIN;//水平触发
epoll_event events;
int epollFD = epoll_create(BACKLOG + 1); // 告诉内核监测的数目, 返回的epollFD为epoll管理句柄
epoll_ctl(epollFD, EPOLL_CTL_ADD, iListenSock, &ev); // 将ev和对应的iListenSock添加到epoll句柄,用于被epollFD管理
while(1)
{
int timeoutMS = -1; // 永不超时
int nfds = epoll_wait(epollFD, &events, BACKLOG + 1, timeoutMS); // events和nfds是一对输出值
printf("nfds is %d\n", nfds);
if(events.data.fd == iListenSock) // 用于监听客户端连接的socket
{
int iConnSock = accept(iListenSock, NULL, NULL);
if (iConnSock < 0)
{
continue;
}
ev.data.fd = iConnSock;
//ev.events = EPOLLIN|EPOLLET;//垂直触发
ev.events = EPOLLIN;//水平触发
epoll_ctl(epollFD, EPOLL_CTL_ADD, iConnSock, &ev); // 将ev和对应的iConnSock添加到epoll句柄,用于被epollFD管理
printf("new sock came, fd is %d\n", iConnSock);
}
else
{
int iConnSock = events.data.fd; // 用于通信的socket
char szBuf[2] = {0}; //使得每次只读一个字节的数据
int recvLen = recv(iConnSock, szBuf, sizeof(szBuf) - 1, 0);
if (recvLen > 0)
{
printf("recv data [%s] from fd [%d]\n", szBuf, iConnSock);
}
else if(0 == recvLen)
{
ev.data.fd = iConnSock;
epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev); //删除ev中对应的句柄
close(iConnSock);
}
else
{
ev.data.fd = iConnSock;
epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev);//删除ev中对应的句柄
close(iConnSock);
}
}
}
close(epollFD);
close(iListenSock);
return 0;
}
客户端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <error.h>
int main()
{
char sendBuf[20]="1234567890";
int ret = 0;
int sockClient = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrSrv;
addrSrv.sin_addr.s_addr=inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888);
ret=connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in));
send(sockClient,sendBuf,strlen(sendBuf),0);
getchar();
close(sockClient);
return 0;
}
先运行服务端,再运行客户端。服务端每次接受一个字节的数据,水平触发的服务端结果如下:
[mapan@localhost IO]$ ./server
nfds is 1
new sock came, fd is 5
nfds is 1
recv data [1] from fd [5]
nfds is 1
recv data [2] from fd [5]
nfds is 1
recv data [3] from fd [5]
nfds is 1
recv data [4] from fd [5]
nfds is 1
recv data [5] from fd [5]
nfds is 1
recv data [6] from fd [5]
nfds is 1
recv data [7] from fd [5]
nfds is 1
recv data [8] from fd [5]
nfds is 1
recv data [9] from fd [5]
nfds is 1
recv data [0] from fd [5]
服务端对客户端发来数据分10次接收了,epoll_wait返回了10次也就接收了10次。
注释代码中的水平触发那一行,解开对垂直触发那行的注释,客户端不变,编译运行。垂直触发的服务端结果如下:
[mapan@localhost IO]$ ./server
nfds is 1
new sock came, fd is 5
nfds is 1
recv data [1] from fd [5]
垂直触发只接收了客户端发来的第一个字节,并没有接收完,就不接收了。
还是代码结果一目了然。