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 ...
随机推荐
- 关于mybatis使用小于号大于号出错的解决方案
原文链接:https://blog.csdn.net/weixin_38061311/article/details/99943807 mybatis 使用的xml的映射文件, 所以里面的标签都是在& ...
- 深入理解Java虚拟机学习笔记(一)-----Java内存区域
一 概述 对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为内一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题 ...
- AsyncOperation和SceneManager.LoadSceneAsync协同加载场景
这篇属于杂记,用于记录不甚理解的AsyncOperation AsyncOperation: //加载进度条 public Silder silder; 加载场景 public void LoginG ...
- linux kernel update
linux内核升级 最近HW行动,报出来的linux系统内核漏洞,环境中全部是2.6.32-431.el6.x86_64的主机,需要全部升级到754版本,这也是第一次进行内核升级操作. 先找了一台和生 ...
- react 使用的方法:
react 使用方法: 第一步: 初始化react 项目 (1)安装node npm (2)npm install --global create-react-app (3)create-react ...
- 【面试篇】寒冬求职之你必须要懂的Web安全
https://segmentfault.com/a/1190000019158228 随着互联网的发展,各种Web应用变得越来越复杂,满足了用户的各种需求的同时,各种网络安全问题也接踵而至.作为前端 ...
- Laravel 中自定义 手机号和身份证号验证
首先在 Providers\AppServiceProvider.php 文件中自定义 手机号和身份证号验证 // AppServiceProvider.php 文件 <?php namespa ...
- 利用oracle数据库闪回功能将oracle数据库按时间点恢复
oracle更新脚本把原数据冲了,并且没有备份,急煞我也 解决办法: oracle数据库有闪回功能: select * from tab 可以查出已被删除的表 ...
- jupyter lab最强代码补全插件
1 简介 提起kite相信不少朋友都有印象,它是一个功能非常强大的代码补全工具,目前可用于Python与javascript,为许多知名的编辑器譬如Vs Code.Pycharm提供对应的插件. 图1 ...
- 痞子衡嵌入式:kFlashFile v1.0 - 一个基于Flash的掉电数据存取方案
大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的个人小项目 - kFlashFile. 痞子衡最近在参与一个基于 i.MXRT1170 的项目,项目有个需求,需要在 Flash 里实 ...