socket采用epoll编程demo
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的更多相关文章
- linux 下 epoll 编程
转载自 Linux epoll模型 ,这篇文章讲的非常详细! 定义: epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显 ...
- tornado 采用 epoll 代理构建高并发网络模型
1 阻塞和非阻塞 对于阻塞和非阻塞,网上有一个很形象的比喻,就是说好比你在等快递,阻塞模式就是快递如果不到,你就不能做其他事情.非阻塞模式就是在这段时间里面,你可以做其他事情,比如上网.打游戏.睡觉 ...
- Java Socket聊天室编程(一)之利用socket实现聊天之消息推送
这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...
- Linux 系统编程 学习:06-基于socket的网络编程1:有关概念
Linux 系统编程 学习:006-基于socket的网络编程1:有关概念 背景 上一讲 进程间通信:System V IPC(2)中,我们介绍了System IPC中关于信号量的概念,以及如何使用. ...
- Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信
Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信 背景 上一讲我们介绍了网络编程的一些概念.socket的网络编程的有关概念 这一讲我们来看UDP 通信. 知识 U ...
- 【Socket编程】通过Socket实现TCP编程
通过Socket实现TCP编程 Socket通信 : 1.TCP协议是面向对象连接.可靠的.有序的,以字节流的方式发送数据. 2.基于TCP协议实现网络通信的类: 客户端----Socket类 服务器 ...
- 【Socket编程】通过Socket实现UDP编程
通过Socket实现UDP编程 UDP通信: 1.UDP协议(用户数据报协议)是无连接.不可靠.无序的. 2.UDP协议以数据报作为数据传输的载体. 3.使用UDP进行数据传输时,首先需要将要传输的数 ...
- Java Socket聊天室编程(二)之利用socket实现单聊聊天室
这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...
- MyBatis的接口式编程Demo
很久没细看过MyBatis了,时间一长就容易忘记. 下面是一个接口式编程的例子. 这里的例子一共分为4步: 1 首先要有一个namespace为接口的全类名的映射文件,该例中是 IMyUser.xml ...
随机推荐
- Java8新特性之流stream
<Java 8 实战>学习笔记系列 定义 流是Java API的新成员,它允许你以声明性方式处理数据集合,可以把它看成遍历数据集的高级迭代器 示例 List<String> t ...
- python学习日记2019.9.2
1 定义一个字符串对象str str.title() #将字符串中用空格分隔的字符段首字母大写 str.rstrip() #将字符串末的空格删去 str.strip() #将字符串首末的空格删去 st ...
- Perl入门(三)Perl的数组
Perl数组的声明方式 Perl使用"@"符号声明一个数组:@array: 使用"()"或"qw()"声明数组中元素: 一个完整的声明方式为 ...
- JavaWeb网上图书商城完整项目--day02-20.修改密码各层实现
1.我们来看看后台操作的业务流程 每一层都按照上面的步骤来进行实现: 这里我们要使用commUtils.toBean把表单提交的参数封装成User对象,必须保证User对象中的字段和表单提交的字段的名 ...
- Spring IoC bean 的加载
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- Flask-install-python2.6
命令: # 安装virtualenv $ sudo yum install python-setuptools $ sudo easy_install virtualenv OR sudo pip i ...
- openstack Rocky 社区版部署1.4 安装数据库
在控制节点安装mariadb,也可以单独服务器安装数据库,假如多个控制节点就在第一台安装数据库,计算节点不需要安装. 1 安装mariadb相关安装包. yum install mariadb mar ...
- python高阶-Linux基础命令集
声明: 1)仅作为个人学习,如有冒犯,告知速删! 2)不想误导,如有错误,不吝指教! 1: 查看文件信息:ls ls常用参数: 参数 含义 -a 显示指定目录下所有子目录与文件,包括隐藏文件 -l 以 ...
- java中的四种访问权限是?
1.私有权限(private) private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类).被private修饰的成员,只能在定义它们的类中使用,在其他类中不能调用. ...
- docker 在centos7中设置 DOCKER_OPTS
不同于Ubuntu目录 /etc/default/docker. 在 CentOS7中Docker默认配置的路径在 /usr/lib/systemd/system/docker.service [例如 ...