epoll工作流程

首先,需要调用epoll_create创建epoll;
此后我们就可以进行socket/bind/listen;
然后调用epoll_ctl进行注册;
接下来,就可以通过一个while(1)循环调用epoll_wait来等待事件的发生;
然后循环查看接收到的事件并进行处理;
1)如果事件是sever的socketfd我们就要进行accept,并且把接收到client的socketfd加入到要监听的事件中;
2)如果在监听过程中,需要修改操作方式(读/写),可以调用epoll_ctl来重新修改;
3)如果监听到某一个客户端关闭,那么我就需要再次调用epoll_ctl把它从epoll监听事件中删除。

epoll的结构体
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 */
}; #include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#define SERV_PORT 8802
int main()
{
int i,flag;
int sockfd,clntfd,newfd;
int epfd,nfds;
ssize_t n;
char buffer[1024];
int s = sizeof(struct sockaddr); struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
//定义epoll数据结构
struct epoll_event ev,events[20]; epfd = epoll_create(256); //创建socket,并初始化事件ev
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket error!\n");
return -1;
}
ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET; //注册epoll事件
flag = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
if (flag < 0) {
perror("epoll_ctl error!\n");
return -1;
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl( INADDR_ANY ); flag = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
if (flag < 0) {
perror("bind error!\n");
return -1;
}
printf("bind\n"); flag = listen(sockfd, 20);
if (flag < 0) {
perror("listen error!\n");
return -1;
}
printf("listen\n"); //开始循环
while (1) {
//等待事件发生,返回请求数目
nfds = epoll_wait(epfd, events, 20, 500);
//一次处理请求
for (i = 0; i < nfds; ++i) {
if (events[i].data.fd == sockfd){
clntfd = accept(sockfd, (struct sockaddr*)&clnt_addr,(unsigned int*)&s);
if (clntfd < 0) {
perror("accept error");
continue;
}
printf("accept\n"); char *str = inet_ntoa(clnt_addr.sin_addr);
printf("accepnt the client ip : %s\n",str); //设置文件标识符,设置操作属性:写操作
ev.data.fd = clntfd;
ev.events = EPOLLOUT | EPOLLET;
//向创建的的epoll进行注册写操作
epoll_ctl(epfd, EPOLL_CTL_ADD, clntfd, &ev);
} else if (events[i].events & EPOLLOUT) {
printf("EPOLLOUT\n"); if ((newfd = events[i].data.fd) < 0)
continue;
bzero(buffer,sizeof(buffer));
strcpy(buffer,"welcome to myserver!\n");
flag = send(newfd, buffer, 1024, 0);
if (flag < 0) {
perror("send error");
continue;
}
//修改操作为读操作
ev.data.fd = clntfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, newfd, &ev);
} else if (events[i].events & EPOLLIN) {
printf("EPOLLIN\n"); bzero(buffer,sizeof(buffer));
if ((newfd = events[i].data.fd) < 0)
continue;
if ((n = read(newfd, buffer, 1024)) < 0) {
if (errno == ECONNRESET){
close(newfd);
events[i].data.fd = -1;
printf("errno ECONRESET!\n");
} else {
perror("readbuffer error!\n");
}
} else if (n == 0) {//表示客户端已经关闭
close(newfd);
events[i].data.fd = -1;
printf("n为0\n");
}
if (buffer[0] != '0')
printf("have read: %s\n", buffer);
}
}
}
close(sockfd);
return 0;
}

  

引自:https://www.bbsmax.com/A/l1dymR3Gde/
优化
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h> #define SERV_PORT 8802
#define MAX_EVENTS 20 int main()
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0); //若成功则返回非负描述符,若失败则返回-1,第一个参数指明协议族(IPv4或IPv6等)
if (listen_fd < 0) { //第二个参数指明套接字类型,字节流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)
perror("socket error!\n");
return -1;
} int epfd = epoll_create(256); //参数会被忽略,但是要大于0,
//若成功返回一个大于 0 的值,表示 epoll 实例;若返回 -1 表示出错 //针对监听的sockfd,创建epollevent
struct epoll_event event;
event.data.fd = listen_fd;
event.events = EPOLLIN | EPOLLET; //注册epoll事件
int flag = epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event); //int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
if (flag < 0) { //成功返回0,出错返回-1
perror("epoll_ctl error!\n");
return -1;
} if (bindAndListenFd(listen_fd) < 0)
return -1; //定义epoll数据结构
struct epoll_event events[MAX_EVENTS]; //可以使用vector,参见muduo源码中的使用 while (1) {
//等待事件发生,返回请求数目
int nfds = epoll_wait(epfd, events, MAX_EVENTS, 500); //maxevents: 返回的events的最大个数,如果最大个数大于实际触发的个数,则下次epoll_wait的时候仍然可以返回
//int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
//成功返回的是一个大于 0 的数,表示事件的个数;返回 0 表示的是超时时间到;若出错返回 -1. for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == listen_fd) {
struct sockaddr_in client_addr;
int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)); //若成功则为非负描述符,若出错则返回-1
if (client_fd < 0) {
perror("accept error");
continue;
} char *str = inet_ntoa(client_addr.sin_addr);
printf("accept the client ip : %s\n",str); onRecvNewConnect(epfd, client_fd); } else if (events[i].events & EPOLLOUT) {
int sockfd = events[i].data.fd;
if (sockfd < 0)
continue; onWriteFd(epfd, sockfd); } else if (events[i].events & EPOLLIN) {
int sockfd = events[i].data.fd;
if (sockfd < 0)
continue; if (onReadFd(epfd, sockfd) < 0) {
events[i].data.fd = -1;
}
}
}
}
close(sockfd);
return 0;
} int bindAndListenFd(int sockfd) {
::fcntl(sockfd, F_SETFL, O_NONBLOCK); //设置非阻塞模式 struct sockaddr_in serv_addr; bzero(&serv_addr, sizeof(serv_addr)); //void bzero(void *dest, size_t nbytes);
serv_addr.sin_family = AF_INET; //类似void *memset(void *dest, int c, size_t len);
serv_addr.sin_port = htons(SERV_PORT); //本地端口号转化为网络端口号 host to network short
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY代表本机所有的IP地址 host to network long int flag = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); //成功返回0,出错返回-1
if (flag < 0) {
perror("bind error!\n");
return -1;
} flag = listen(sockfd, 20); //成功返回0,出错返回-1
if (flag < 0) {
perror("listen error!\n");
return -1;
} return 0;
} void onRecvNewConnect(int epfd, int clientfd) {
::fcntl(sockfd, F_SETFL, O_NONBLOCK); //设置非阻塞模式
//设置文件标识符,设置操作属性:写操作
struct epoll_event ev_client;
ev_client.data.fd = clintfd;
ev_client.events = EPOLLOUT | EPOLLET;
//向创建的的epoll进行注册写操作
epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev_client);
} void onWriteFd(int epfd, int sockfd) {
char buffer[1024];
bzero(buffer, sizeof(buffer));
strcpy(buffer, "welcome to myserver!\n");
int flag = send(sockfd, buffer, 1024, 0);
if (flag < 0) {
perror("send error");
return;
}
//修改操作为读操作
struct epoll_event ev_client;
ev_client.data.fd = sockfd;
ev_client.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev_client);
} int onReadFd(int epfd, int sockfd) {
char buffer[1024];
bzero(buffer, sizeof(buffer));
int n = read(sockfd, buffer, 1024);
if (n < 0) {
if (errno == ECONNRESET) {
close(sockfd);
printf("errno ECONRESET!\n");
return -1;
} else {
perror("readbuffer error!\n");
}
} else if (n == 0) { //表示客户端已经关闭
close(sockfd);
printf("n为0\n");
return -1;
}
if (buffer[0] != '0')
printf("have read: %s\n", buffer);
return 0;
}

socket采用epoll编程demo的更多相关文章

  1. linux 下 epoll 编程

    转载自 Linux epoll模型 ,这篇文章讲的非常详细! 定义: epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显 ...

  2. tornado 采用 epoll 代理构建高并发网络模型

    1 阻塞和非阻塞  对于阻塞和非阻塞,网上有一个很形象的比喻,就是说好比你在等快递,阻塞模式就是快递如果不到,你就不能做其他事情.非阻塞模式就是在这段时间里面,你可以做其他事情,比如上网.打游戏.睡觉 ...

  3. Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...

  4. Linux 系统编程 学习:06-基于socket的网络编程1:有关概念

    Linux 系统编程 学习:006-基于socket的网络编程1:有关概念 背景 上一讲 进程间通信:System V IPC(2)中,我们介绍了System IPC中关于信号量的概念,以及如何使用. ...

  5. Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信

    Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信 背景 上一讲我们介绍了网络编程的一些概念.socket的网络编程的有关概念 这一讲我们来看UDP 通信. 知识 U ...

  6. 【Socket编程】通过Socket实现TCP编程

    通过Socket实现TCP编程 Socket通信 : 1.TCP协议是面向对象连接.可靠的.有序的,以字节流的方式发送数据. 2.基于TCP协议实现网络通信的类: 客户端----Socket类 服务器 ...

  7. 【Socket编程】通过Socket实现UDP编程

    通过Socket实现UDP编程 UDP通信: 1.UDP协议(用户数据报协议)是无连接.不可靠.无序的. 2.UDP协议以数据报作为数据传输的载体. 3.使用UDP进行数据传输时,首先需要将要传输的数 ...

  8. Java Socket聊天室编程(二)之利用socket实现单聊聊天室

    这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...

  9. MyBatis的接口式编程Demo

    很久没细看过MyBatis了,时间一长就容易忘记. 下面是一个接口式编程的例子. 这里的例子一共分为4步: 1 首先要有一个namespace为接口的全类名的映射文件,该例中是 IMyUser.xml ...

随机推荐

  1. Dubbo——SPI及自适应扩展原理

    文章目录 引言 正文 一.什么是SPI? 1. Java SPI的实现 2. Dubbo SPI实现原理 由配置文件得到的猜想 SPI源码 二.自适应扩展机制 三.Dubbo IOC 总结 引言 Du ...

  2. CSS sprites的定义及使用

    定义:CSS sprites 其实就是把网页中的一些背景图片整合到一张图片文件中,再利用CSS的“background-image”.“background-repeat”.“background-p ...

  3. 连接 mongodb 数据库 :

    mongodb  数据库: 安装 mongodb  数据库: 安装 mongodb 数据库网址: https://www.mongodb.com/download-center#community 检 ...

  4. Java前端面试题总结

    Java前端面试题总结 简单说一下HTML,CSS,javaScript在网页开发中的定位? HTML:超文本标记语言,定义网页的结构 CSS:层叠样式表,用来美化页面 JavaScript:主要用来 ...

  5. java之SFTP上传下载

    import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.ut ...

  6. 黎活明8天快速掌握android视频教程--25_网络通信之资讯客户端

    1 该项目的主要功能是:后台通过xml或者json格式返回后台的视频资讯,然后Android客户端界面显示出来 首先后台新建立一个java web后台 采用mvc的框架 所以的servlet都放在se ...

  7. 尚硅谷spring 事物管理

    接下来我们重点来学习spring中的事务管理 接下来我们通过代码来实现 spring实现事物我们采用aop的方式来实现 获得连接和手动设置事物相当于@before标注的前置通知,conn.commit ...

  8. C++核心内容和机制

    备注:不局限与C++版本   一. 基础知识 数据类型和POD/Trivial 数据类型: 类型转换: NULL和nullptr: 操作符重载: 全局静态变量和成员静态变量的申明和初始化: 左值和右值 ...

  9. layer.open弹框中的表单数据无法获取

    layer.open弹框中的表单数据无法获取 表单数据模板 layer.open() 页面效果: 当点击确定后,radio和textarea获取的值总是为空,解决办法: var setPriCustB ...

  10. Python3-shutil模块-高级文件操作

    Python3中的shutil模块提供了对文件和容器文件的一些高级操作 shutil.copy(src, dst) 拷贝文件,src和dst为路径的字符串表示,copy()会复制文件数据和文件权限,但 ...