EPOLLIN和EPOLLOUT究竟什么时候触发?
来源:互联网 发布:mysql数据库去重 编辑:程序博客网 时间:2024/04/28 11:15
server.cpp
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;
#define MAXLINE 1024
#define SERV_PORT 8877
//发生了致命错误,输出错误后立即退出
void error_quit(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
int listenfd, connfd, sockfd, epfd;
int i, res, maxi, nfds;
ssize_t n = 10;
char buf[MAXLINE] = "world";
socklen_t clilen;
struct sockaddr_in cliaddr;
struct sockaddr_in servaddr;
//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
struct epoll_event ev, events[256];
//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大
epfd = epoll_create(256);
if( -1 == epfd )
error_quit("epoll_create error");
//创建用于TCP协议的套接字
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
//把socket和socket地址结构联系起来
res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if( -1 == res )
error_quit("bind error");
//开始监听LISTENQ端口
res = listen(listenfd, INADDR_ANY);
if( -1 == res )
error_quit("listen error");
//设置与要处理的事件相关的文件描述符和事件
ev.data.fd = listenfd;
/*
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
*/
ev.events = EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev);
maxi = 0;
while(1)
{
//等待epoll事件的发生
//返回需要处理的事件数目nfds,如返回0表示已超时。
nfds = epoll_wait(epfd, events, 20, 500);
//处理所发生的所有事件
for(i=0; i < nfds; ++i)
{
//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
if(events[i].data.fd == listenfd)
{
connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &clilen);
if( -1 == connfd )
error_quit("accept error");
//注册用于读操作的文件描述符和事件
ev.data.fd = connfd;
ev.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLERR|EPOLLHUP;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
if( -1 == res )
error_quit("epoll_ctl error");
}
//如果有数据发送
else if(events[i].events & EPOLLOUT)
{
cout << "EPOLLOUT event" << endl;
sockfd = events[i].data.fd;
res = write(sockfd, buf, 10 );
cout << "write " << res << " bytes. errno" << errno << ", str is "
<< strerror( errno ) << endl;
//注册用于读操作的文件描述符和事件
/*ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
if( -1 == res )
error_quit("epoll_ctl error");
*/
}
//如果是已经连接的用户,并且收到数据,那么进行读入。
else if(events[i].events & EPOLLIN)
{
cout << "EPOLLIN event" << endl;
sockfd = events[i].data.fd;
if ( sockfd < 0 )
continue;
n = read(sockfd, buf, MAXLINE);
if ( n < 0)
{
// Connection Reset:你连接的那一端已经断开了,
//而你却还试着在对方已断开的socketfd上读写数据!
if (errno == ECONNRESET)
{
close(sockfd);
events[i].data.fd = -1;
}
else
error_quit("read error");
}
//如果读入的数据为空
else if ( n == 0 )
{
close(sockfd);
events[i].data.fd = -1;
}
/*
else
{
//注册用于写操作的文件描述符和事件
ev.data.fd = sockfd;
ev.events = EPOLLOUT|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
if( -1 == res )
error_quit("epoll_ctl error");
}
*/
} //如果有数据发送
else if(events[i].events & EPOLLERR)
{
cout << "EPOLLERR event" << endl;
}
else if(events[i].events & EPOLLHUP)
{
cout << "EPOLLHUP event" << endl;
}
}
}
return 0;
}
client.cpp
#include <stdio.h>
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
using namespace std;
int main ( int argc, char ** argv )
{
if ( argc < 5 )
{
cout << "Usage : ip port kind need_crlf str, like : 172.27.201.182 39752 tcp Y hello" << endl;
return -1;
}
int iRet = -1;
string sHost = argv[1];
int iPort = atoi( argv[2] );
string sKind = argv[3];
string sNeedCRLF = argv[4];
string sIn = argv[5];
int sock_fd = -1, serverlen;
struct sockaddr_in server_addr;
if ( sKind == "udp" )
{
sock_fd = socket( AF_INET, SOCK_DGRAM, 0 );
}
else if ( sKind == "tcp" )
{
sock_fd = socket( AF_INET, SOCK_STREAM, 0 );
}
if ( sock_fd == -1 )
{
cout << "create socket failed !!" << endl;
return -1;
}
bzero( &server_addr, sizeof ( struct sockaddr_in ) );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( iPort );
server_addr.sin_addr.s_addr = inet_addr( sHost.c_str() );
serverlen = sizeof ( server_addr );
if ( sNeedCRLF == "Y" )
{
sIn = sIn + "\r\n";
}
int iLen = sIn.length() ;
if ( sKind == "tcp" )
{
iRet = connect( sock_fd, (struct sockaddr *)&server_addr, serverlen );
if ( iRet != 0 )
{
cout << "connect failed" << endl;
return -1;
}
}
cout << "connect to server " << sHost << ":" << iPort << endl;
sleep( 10 );
if ( sendto( sock_fd, sIn.c_str(), iLen, 0, (struct sockaddr *)&server_addr, serverlen ) < 0)
{
cout << "send to server failed !!" << endl;
close( sock_fd );
return -2;
}
cout << "send to server " << sHost << ":" << iPort << endl;
char sBuffer[1024] ;
sleep( 10 );
int iSize = ::recvfrom( sock_fd, sBuffer, 1024 , 0,
(struct sockaddr*)&server_addr, (socklen_t*)&serverlen);
cout << "recv return: " << iSize << " bytes " << endl;
cout << sBuffer << endl;
sleep( 10 );
close( sock_fd );
cout << " send finished, close socket. " << endl;
return 0;
}
开一个窗口执行server
./server
再开一个窗口执行client
这时server端会打印
EPOLLOUT event
write 10 bytes. errno0, str is Success
过10秒
EPOLLOUT event
write 10 bytes. errno0, str is Success
过10秒
下面三行同时打印
EPOLLOUT event
write 10 bytes. errno0, str is Success
EPOLLIN event
可见 如果同时监听EPOLLIN|EPOLLOUT,客户端connect的时候会产生一次EPOLLOUT|EPOLLIN;客户端send的时候会产生一次EPOLLOUT|EPOLLIN;客户端recv的时候没有任何事件产生,客户端关闭时会产生EPOLLOUT|EPOLLIN|EPOLLHUP|EPOLLERR四个事件
- EPOLLIN和EPOLLOUT究竟什么时候触发?
- EPOLLOUT和EPOLLIN触发时刻
- 简述Linux Epoll ET模式EPOLLOUT和EPOLLIN触发时刻
- 简述Linux Epoll ET模式EPOLLOUT和EPOLLIN触发时刻
- 简述Linux Epoll ET模式EPOLLOUT和EPOLLIN触发时刻
- EPOLLIN , EPOLLOUT , EPOLLPRI, EPOLLERR 和 EPOLLHUP事件
- EPOLLIN , EPOLLOUT , EPOLLPRI, EPOLLERR 和 EPOLLHUP事件
- select.epoll多路复用web服务器---sock.fileno;select.EPOLLIN;select.EPOLLOUT;
- send() 和 EPOLLOUT 的关系
- finally究竟什么时候执行
- 新手求教,请问ACTION_DOWN和ACTION_UP什么时候触发呢?
- dbwr什么时候触发?
- layoutSubviews什么时候触发调用
- layoutSubviews什么时候触发
- 什么时候触发GC
- 关于epoll边缘触发模式(ET)下的EPOLLOUT触发
- -(void)layoutSubviews什么时候触发调用
- onSaveInstanceState与onRestoreInstanceState什么时候触发
- [java]Struts2防止用户重新登陆的实现
- BO3.1Script
- Android webkit image的加载过程解析(二)
- 交流群里遇到的一个小问题
- 解决“Unable to get debug signature key”的办法
- EPOLLIN和EPOLLOUT究竟什么时候触发?
- C++父类定义虚函数其派生类的同名函数使用virtual关键字吗?
- [JAVA]Hibernate 有选择性的更新--控制insert和update语句
- C#版二维码生成器附皮肤下载
- LeetCode Longest Palindromic Substring 最长回文子字符串 两种方法分析解答
- js设置汉字为utf-8的编码
- JDBC读写Oracle10g的CLOB、BLOB
- Mac OS, iOS多线程开发指南,apple官方文档翻译稿
- JAVA System.getProperty("") 获取当前操作系统信息