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 ...
随机推荐
- 01 . ELK Stack简介原理及部署应用
简介 ELK并不是一款软件,是一整套解决方案,是由ElasticSearch,Logstash和Kibana三个开源工具组成:通常是配合使用,而且先后归于Elastic.co公司名下,简称ELK协议栈 ...
- Python初识类与对象
Python初识类与对象 类与对象 世界观角度分析类与对象 类是一个抽象的概念,而对象是一个实体的存在,对象由类创造而出,每个对象之间互相独立互不影响,一个对象可以同时拥有多个类的方法,实例化就是通过 ...
- Zookeeper分布式过程协同技术 - 群首选举
Zookeeper分布式过程协同技术 - 群首选举 群首概念 群首为集群中服务器选择出来的一个服务器,并被集群认可.设置群首目的在与对客户端所发起的状态变更请求进行排序,包括:create.setDa ...
- 入门大数据---Flume整合Kafka
一.背景 先说一下,为什么要使用 Flume + Kafka? 以实时流处理项目为例,由于采集的数据量可能存在峰值和峰谷,假设是一个电商项目,那么峰值通常出现在秒杀时,这时如果直接将 Flume 聚合 ...
- 入门大数据---Kafka深入理解分区副本机制
一.Kafka集群 Kafka 使用 Zookeeper 来维护集群成员 (brokers) 的信息.每个 broker 都有一个唯一标识 broker.id,用于标识自己在集群中的身份,可以在配置文 ...
- 入门大数据---Python基础
前言 由于AI的发展,包括Python集成了很多计算库,所以淡入了人们的视野,成为一个极力追捧的语言. 首先概括下Python中文含义是蟒蛇,它是一个胶水语言和一个脚本语言,胶水的意思是能和多种语言集 ...
- vue的生命函数周期以及钩子函数的详解
首先我们先附上官网的图 图中展现出的是vue整个生命周期以及钩子函数 1- beforeCreate(创建前) 2- created(创建完成) 3- beforeMount(挂载前) 4- mo ...
- 实战笔记丨JDBC问题定位指南
JDBC(Java数据库连接性)是Java API,用于管理与数据库的连接,发出查询和命令以及处理从数据库获得的结果集.JDBC在1997年作为JDK 1.1的一部分发布,是为Java持久层开发的首批 ...
- MongoDB快速入门教程 (3.3)
3.4.聚合 3.4.1.什么是聚合? MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果.有点类似sql语句中的 count(*) 例如上图 ...
- IIS 发布页面后或者vs平台运行后显示“未能加载文件或程序集“WebApi”或它的某一个依赖项。试图加载格式不正确的程序。”
一般情况下出现这样的问题是因为.dll文件不存在或者路径不正确. 但今天我遇到的情况都不在这两个内. 我确定.dll文件是存在的,路径也是正确的. 但是程序死活都是“未能加载文件或程序集“xxx”或它 ...