linux send 失败_Epoll学习服务器的实现-Linux内核原始Epoll结构

1.Begins~

有的人学习linux编程很久,只知道网络编程是socket,bind, listen。。。,然而这些都是网络通信软件最基本的接口。在某网络公司待了y,也了解到公司的基础就是网络转发 ,然而网络转发实现并非我们平时所见的简单的send,recv。公司设备的转发都是建立在稳定并且高效的内部业务的基础上的,例如一个可靠性的服务:BFD(链路双向快速检测),进程内业务就是建立在内核与用户态,后台处理与前台配置交互的过程上实现。在做项目的日子里,渐渐的对公司BFD模块有一定了解,特别是涉及到框架的地方,仔细看发现,一些技巧其实就是我们平日所用的技能外加一些优化。对于单进程用户态来说,处理前台进程交互与内核上送消息,如果没有一个高效的机制,是很难处理上以千计甚至万计的消息的。在参看公司代码的初始化阶段,发现一共用了至少3 个epoll回调机制,一个是处理前台发送的配置及查询,一个是处理与其他应用模块的交互,另一个是向其他应用通报状态变化的机制。公司的epoll_event 机制不知道是不是修改了内核,结构大致是这样:

typedef struct epoll_event { uint32_t events;      // Epoll events  epoll_data_t data;      // User data variable ep_callback  callback;    /* 这跟linux内核不一样 ? */}EPOLL_PACK; /*在添加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  };  

不过我们也可以将epoll_data_t这个联合体封装在一个结构,只需要用指针*ptr指向封装的带fd及回调函数就可以。打算自己学习一些必要的技能,暂且就记录一下吧

2.Epoll API


extern int epoll_create(int  size);extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);extern int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

① int epoll_create(int size);
创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,
它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll
后,必须调用close()关闭,否则可能导致fd被耗尽。
②int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先
注册要监听的事件类型。
参数:
第一个参数是epoll_create()的返回值。
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

enum EPOLL_EVENTS{

EPOLLIN = 0x001,

#define EPOLLIN EPOLLIN

EPOLLPRI = 0x002,

#define EPOLLPRI EPOLLPRI

EPOLLOUT = 0x004,

#define EPOLLOUT EPOLLOUT

EPOLLRDNORM = 0x040,

#define EPOLLRDNORM EPOLLRDNORM

EPOLLRDBAND = 0x080,

#define EPOLLRDBAND EPOLLRDBAND

EPOLLWRNORM = 0x100,

#define EPOLLWRNORM EPOLLWRNORM

EPOLLWRBAND = 0x200,

#define EPOLLWRBAND EPOLLWRBAND

EPOLLMSG = 0x400,

#define EPOLLMSG EPOLLMSG

EPOLLERR = 0x008,

#define EPOLLERR EPOLLERR

EPOLLHUP = 0x010,

#define EPOLLHUP EPOLLHUP

EPOLLRDHUP = 0x2000,

#define EPOLLRDHUP EPOLLRDHUP

EPOLLWAKEUP = 1u << 29,

#define EPOLLWAKEUP EPOLLWAKEUP

EPOLLONESHOT = 1u << 30,

#define EPOLLONESHOT EPOLLONESHOT

EPOLLET = 1u << 31

#define EPOLLET EPOLLET

};

(3)epoll_wait();

参数events是分配好的epoll_event结构体数组;
epoll将会把发生的事件复制到events数组中(events不可以是空指针,内核只负责把数据复制到)
参数timeout 是超时时间(毫秒,0表示立即返回,-1表示永久阻塞);
如果函数调用成功,返回对应IO上已经准备好的文件描述符数目;如果返回0,表示已超时;如果返回值小于0,表示函数失败。

Linuxc/c++服务器开发高阶视频学习资料+主页群获取

30b3471d5199dd42de917a34f9cfdf78.png

3.epoll mode

epoll的使用也很方便,基本都会用到以下模型,取自linux帮助手册:

/*Example for suggested usageWhile  the  usage  of  epoll when employed as a level-triggered interface does have the same semantics as poll(2), the edge-triggered usage requires moreclarification to avoid stalls in the application event loop.  In this example, listener is a nonblocking socket on which listen(2) has been called.   Thefunction  do_use_fd()  uses the new ready file descriptor until EAGAIN is returned by either read(2) or write(2).  An event-driven state machine applica‐tion should, after having received EAGAIN, record its current state so that at the next call to do_use_fd() it will continue to read(2) or write(2)  fromwhere it stopped before.*/#define MAX_EVENTS 10struct epoll_event ev, events[MAX_EVENTS];int listen_sock, conn_sock, nfds, epollfd;/* Code to set up listening socket, 'listen_sock',(socket(), bind(), listen()) omitted */static Epoll_Case(){epollfd = epoll_create1(0);if (epollfd == -1) {perror("epoll_create1");exit(EXIT_FAILURE);}ev.events = EPOLLIN;ev.data.fd = listen_sock;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {perror("epoll_ctl: listen_sock");exit(EXIT_FAILURE);}for (;;) {nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);if (nfds == -1) {perror("epoll_wait");exit(EXIT_FAILURE);}for (n = 0; n < nfds; ++n) {if (events[n].data.fd == listen_sock) {conn_sock = accept(listen_sock,(struct sockaddr *) &local, &addrlen);if (conn_sock == -1) {perror("accept");exit(EXIT_FAILURE);}setnonblocking(conn_sock);ev.events = EPOLLIN | EPOLLET;ev.data.fd = conn_sock;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev) == -1) {perror("epoll_ctl: conn_sock");exit(EXIT_FAILURE);}} else {do_use_fd(events[n].data.fd);}}}}/*           When  used  as an edge-triggered interface, for performance reasons, it is possible to add the file descriptor inside the epoll interface (EPOLL_CTL_ADD)once by specifying (EPOLLIN|EPOLLOUT).  This allows you  to  avoid  continuously  switching  between  EPOLLIN  and  EPOLLOUT  calling  epoll_ctl(2)  withEPOLL_CTL_MOD.*/

下面给出一个epoll 服务器的简单例子,参考了网上不少方式,自己写了一个,算不上原创,把BIT_TEST部分注释打开,可以忽略basetype.h,可以编译通过。创建socket的地方可以自己优化省略,我实现的时候觉得这种方式比较容易看清楚socket的获取方式。


#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <string.h>#include <netinet/in.h>#include <sys/epoll.h>#include <poll.h>#include <assert.h>#include <unistd.h>#include <fcntl.h>#include "basetype.h"static int g_Main_Epoll_Fd = -1;#define MAX_EPOLL_EVENT_COUNT 64#define MAX_LISTEN_NUM  10#define MAX_BUFF_LEN 512/*#define BIT_TEST(a, b)          ((a) & (b))#define BIT_RESET(a, b)         ((a) ~= (b))#define BIT_SET(a, b)           ((a) |= (b))#define BIT_MATCH(a,b)          ((a)&(b) = (b))#define BIT_COMPARE(a, b, c)    ((a)&(b) == (c))*/static void print_socket_info(struct addrinfo *ai){char ipstr[INET6_ADDRSTRLEN];uint16_t port;void *addr  = NULL;char *ipver = NULL;struct sockaddr_in *ipv4 = NULL;struct sockaddr_in6 *ipv6 = NULL;assert(NULL != ai);if (ai->ai_family == AF_INET) { // IPv4ipv4 = (struct sockaddr_in *)ai->ai_addr;addr = &(ipv4->sin_addr);ipver = "IPv4";port =ntohs(((struct sockaddr_in*)ai->ai_addr)->sin_port);} else { // IPv6ipv6 = (struct sockaddr_in6 *)ai->ai_addr;addr = &(ipv6->sin6_addr);ipver = "IPv6";port =ntohs(((struct sockaddr_in6*)ai->ai_addr)->sin6_port);}// convert the IP to a string and print it:(void)inet_ntop(ai->ai_family, addr, ipstr, sizeof ipstr);printf(" server initing... AF: %s IP: %s,PORT: %un", ipver, ipstr,port);return ;}static int SetSocketNoblocking(const int sockfd){int flags = 0;int iret  = 0;flags = fcntl (sockfd, F_GETFL, 0);if (flags == -1){perror ("fcntl");return -1;}flags |= O_NONBLOCK;iret = fcntl (sockfd, F_SETFL, flags);if (iret == -1){perror ("fcntl");return -1;}return 0;}static int Epoll_Control(const int sockfd, const int oper, const int events){struct epoll_event ep_event;int iret = 0;memset(&ep_event, 0, sizeof(ep_event));ep_event.events = events;ep_event.data.fd = sockfd;iret = epoll_ctl(g_Main_Epoll_Fd, oper, sockfd, &ep_event);if (0 > iret){perror("epoll_ctl error.");printf("epoll_ctl error opt:%dn", oper);return -1;}return 0;}static int CreateServerScoket(const char *host, const char *port){struct addrinfo hints;struct addrinfo*ailist= NULL; struct addrinfo *ai= NULL;int iret;int sockfd = 0;int sockoptval = 1;memset(&hints, 0, sizeof(hints));hints.ai_family = AF_UNSPEC; // AF_INET 或 AF_INET6 hints.ai_socktype = SOCK_STREAM;hints.ai_flags = AI_PASSIVE;     /* All interfaces */iret = getaddrinfo(host, port, &hints, &ailist);if (0 != iret){fprintf(stderr, "getaddrinfo: %sn", gai_strerror(iret));return -1;}for(ai = ailist;ai != NULL; ai = ai->ai_next) {sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);if (0 > sockfd){continue;}/* set reuse addr */iret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(sockoptval));if (0 > iret){perror("setsockopt errorn");freeaddrinfo(ailist);return -1;}iret = bind(sockfd, ai->ai_addr, ai->ai_addrlen);/*sizeof(struct sockaddr)*/if (0 == iret){print_socket_info(ai);break;}else{close(sockfd);}}if (NULL == ai){fprintf(stderr, "get server socket error.n");return -1;}freeaddrinfo(ailist); /* set no-block mode */iret = SetSocketNoblocking(sockfd);if (0 != iret){return -1;}/* listen socket */iret = listen(sockfd, MAX_LISTEN_NUM);if (0  > iret){perror("listen socket error.n");return -1;}printf("server socket init success. listenning...n");return sockfd;}static void HandleAccept(const int listen_scok, int Event){struct sockaddr_in addr;socklen_t  len = sizeof(addr);int client_sock  = -1;if (BIT_TEST(EPOLLIN, Event)){client_sock = accept(listen_scok, (struct sockaddr *)&addr, &len);if(client_sock < 0 ){perror("accept infd error");return;}(void)Epoll_Control(client_sock, EPOLL_CTL_ADD, EPOLLIN);}return ;}static void ProcRecvMsg(int sockfd){ssize_t recvlen = 0;char recvBuf[MAX_BUFF_LEN];//read ev  ready;recvlen = recv(sockfd, recvBuf, sizeof(recvBuf), 0);if(recvlen < 0){perror("recv error.");return ;}else if(recvlen == 0){printf("client quitn");	(void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0);close(sockfd);}else{(void)Epoll_Control(sockfd, EPOLL_CTL_MOD, EPOLLOUT);printf("recv msg fprom client:msg#:%sn",recvBuf);}return ;}static void SendMsg2Client(const int sockfd){ssize_t sendlen = 0;char sendBuf[MAX_BUFF_LEN];int event = 0;sprintf(sendBuf, "HTTP/1.0 200 OKrnrn<html><h1>Hello Epoll! [client_fd:%d]</h1></html>", sockfd);  int sendsize = send(sockfd, sendBuf, strlen(sendBuf)+1, 0);  if(sendsize <= 0)  {  perror("send error.");(void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0);close(sockfd);}  else {  printf("Server reply msg ok! data: %sn", sendBuf);  event = EPOLLIN | EPOLLERR | EPOLLHUP;(void)Epoll_Control(sockfd, EPOLL_CTL_MOD, event);}  return ;}int main(int argc, char *argv[]){int listenfd =0;int epfd = 0;int iret = 0;int event = 0;struct epoll_event ep_events[MAX_EPOLL_EVENT_COUNT];int ev_num = 0;int in_sockfd = 0;int loop = 0;if (3 > argc){printf("Usage: %s [ip_addr] [port]n",argv[0]);return -1;}listenfd = CreateServerScoket(argv[1], argv[2]);if (0 > listenfd){return -1;}epfd = epoll_create(1);if (-1 == epfd){perror("create epoll error.n");return -1;}else{g_Main_Epoll_Fd = epfd;}event = EPOLLIN;iret = Epoll_Control(listenfd, EPOLL_CTL_ADD, event);if (0 != iret){return -1;}/* proc epoll event regined */printf("server init success, epoll wait now.n");for(;;){memset(ep_events, 0, sizeof(ep_events));ev_num = epoll_wait(g_Main_Epoll_Fd, ep_events, MAX_EPOLL_EVENT_COUNT, -1);switch (ev_num){case 0:{printf("epoll_wait timeout.n");break;}case -1:{perror("epoll_wait error.n");break;}default:{for (loop = 0; loop < ev_num; loop++){in_sockfd = ep_events[loop].data.fd;event = ep_events[loop].events;if (in_sockfd == listenfd){HandleAccept(in_sockfd, event);}else{if (BIT_TEST(EPOLLIN, event)){/* have data to read */ProcRecvMsg(in_sockfd);}else if (BIT_TEST(EPOLLOUT, event)){SendMsg2Client(in_sockfd);}}}break;}}}return 0;}

结果如下,客户端任意都可以:注意,运行时需要手动输入服务器主机名及端口号。

5f13c51a4ea7659327e3d034d59b1e92.png

原文链接:Epoll学习服务器的简单实现-Linux内核原始Epoll结构_li-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/529447.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

市面上有哪几种门_选择 low-e门窗玻璃,你想知道的都在这了

最近系统门窗群里&#xff0c;不少读者粉丝在咨询关于Low-e玻璃的问题&#xff0c;大家越来越重视好的门窗对于隔热、隔音、隔辐射的作用。我汇总了群里大家咨询最多的20个问题&#xff0c;希望对大家在挑选门窗玻璃时起到一些帮助。在介绍low-e玻璃前&#xff0c;大家先跟着我…

arm中断保护和恢复_嵌入式ARM系统异常和中断处理知识总结

关于异常处理&#xff0c;分为三部分&#xff1a; 1. ARM异常和模式&#xff1a;core处理异常时的操作&#xff0c;几种模式介绍。 2. Vector table&#xff1a; 3. 异常优先级 4. lr偏移&#xff1a;几种异常如何返回异常和中断处理简介在嵌入式系统中异常处理是核心之一。高效…

计算机工作站 曙光,曙光桌面工作站解析

曙光桌面工作站内部架构从整个结构设计来看&#xff0c;整机采用10升小机箱设计&#xff0c;尺寸为326x90x333.4mm&#xff0c;重量在5KG左右&#xff1b;支持垂直放置或横置&#xff0c;节省办公空间。接口方面&#xff0c;整机在前面板提供了4个USB 3.0接口&#xff0c;后置接…

查看网卡命令 uefi_大神教你开机进入UEFI模式的方法,非常简单

启动电脑时&#xff0c;如果要更改启动优先级、启用或禁用安全启动或更改任何其他低级设置&#xff0c;则需要启动到UEFI模式。根据主板制造商的不同&#xff0c;必须按键盘上的特定键或组合键才能进入UEFI模式。尽管初始屏幕显示了需要按下的键&#xff0c;但对于初学者来说却…

计算机网络拓扑结构说课稿,计算机网络应用基础说课稿修稿稿

计算机网络应用基础说课稿修稿稿 (6页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;19.90 积分计算机网络的分类说课稿第1页共6页计算机网络的分类说课稿蓬溪县中等职业技术学校唐湘各位评委老师大家…

扩展源_瑞萨电子推出具备反向充电WattShare TRx模式的 15W无线充电电源P9415R接收器,扩展无线电源产品线...

News全球领先的半导体解决方案供应商瑞萨电子集团(TSE&#xff1a;6723)日前宣布推出采用瑞萨独有WattShareTM技术的P9415-R无线电源接收器&#xff0c;作为其无线电源解决方案的最新成员。新型15W无线电源接收器使智能手机、移动电源以及便携式工业和医疗设备能够对其它具有无…

postgresql存图片字段类型_PostgreSQL 入门

安装、设置、创建和开始使用 PostgreSQL 数据库。-- Greg Pittman&#xff08;作者&#xff09;每个人或许都有需要在数据库中保存的东西。即使你执着于使用纸质文件或电子文件&#xff0c;它们也会变得很麻烦。纸质文档可能会丢失或混乱&#xff0c;你需要访问的电子信息可能会…

ibm服务器 p5 硬盘,IBM虚拟化实战之p5服务器逻辑分区解读

创建 AIX 分区现在您将创建一个 AIX 分区&#xff0c;它使用 VIO Server 的 SEA 来访问网络。它还将其 rootvg 寄宿在 VIO LPAR 上。创建 AIX 服务器分区和概要除了下面的变化以外&#xff0c;创建 AIX 分区和概要的步骤与创建VIO Server 分区和概要部分中的步骤完全相同。1. 在…

cad动态块制作翻转_装X必学,手把手教你做CAD动态属性块

最近很多粉丝后台问熊大&#xff0c;如何制作CAD动态属性块&#xff0c;这个问题把我难到了。因为这个不是一句两句话能够说得清楚的&#xff0c;于是我拿出珍藏的“西蜀鄙夫”写的教程&#xff0c;并通过与我们设计过程的实际需要给大家说说。干货文章&#xff0c;大家耐心看完…

网和aoe网的区别_运动内衣与普通内衣有什么区别?运动内衣里面还需要穿文胸吗? 小家生活网20201002 11:03:04...

阅读本文前&#xff0c;请您先点击上面的“蓝色字体可以说爱情就是他们最大的软肋。不会在双鱼女那出现。在对于周边的亲戚时。她们那种温柔大方的气势。更符合妈妈的需求。整双腿没有视觉连贯性。但唯独感情这件事一直是水瓶们的知识盲区。他们双商超高。可能最开始还是会去挽…

机器人无限火力无限e符文_LOL:无限火力开黑指南 三大玩法让你快乐加倍

无限火力上线以来受到大家的热烈追捧&#xff0c;今天给大家介绍几个主流的系列&#xff0c;让你在峡谷能够感受到双倍的快乐&#xff01;超强控制流这一类英雄都是带有强力控制的&#xff0c;无限火力的80%减CD&#xff0c;能够让女坦等英雄可以打出长达几秒的完美控制技能。首…

bootstrap-table toolbar图标换文字_iPhone 也能随意换字体啦~

呔咯昨天从老大手里喜提一台 iPhone XS Max &#xff0c;终于让表妹从安卓阵营转入苹果&#xff0c;嘻嘻~当她拿着 iPhone 的时候&#xff0c;一脸懵的问我&#xff1a;iPhone 自带字体也太丑了吧&#xff0c;还我的输入法&#xff01;&#xff01;&#xff01;呔咯&#xff1a…

网络服务器分为文件服务器通信服务器和,近代中国落后、贫困的根本原因是()...

近代far, find, lights, minutes, to, right, tellA: Excuse me. Could you ____ me the way ____ the bookshop?B: Ok. Go along this road and turn____ at the traffic ____. Then go straight on. Youll ____ it next to the post office.A: Is it ____ from here?B: No,…

搭建bitwarden_Docker轻松部署Bitwarden私有密码管理系统服务

0x01. 什么是Bitwarden?开源免费的密码管理器&#xff0c;Bitwarden的原理是使用高强度的AES256 算法对你的个人数据进行本地加密&#xff0c;然后再传输到云端服务器来实现网络同步。bitwarden使用的是微软的Azure云服务器&#xff0c;利用PBKDF2 加密主密码&#xff0c;而且…

取文件 shell_webshell文件上传分析溯源

由于最近比较忙&#xff0c;暂时没有时间更新&#xff0c;所以分享分享一些其他博主文章&#xff0c;大家敬请谅解摘要&#xff1a; dirbuster扫描目录发现几个有用的东东/admin/upload.php/admin/upload1.php/admin/upload2.php访问的时候最后都会跳转到/admin/upload...dirbu…

7系统软raid_使用图形界面来配置RAID

RAID 配置起来要比 LVM 方便&#xff0c;因为它不像 LVM 那样分了物理卷、卷组和逻辑卷三层&#xff0c;而且每层都需要配置。我们在图形安装界面中配置 RAID 1和 RAID 5&#xff0c;先来看看 RAID 1 的配置方法。RAID 1 配置配置 RAID 1 时同样需要启动图形安装程序&#xff0…

合并相同数据的行_R语言笔记(六):数据框重塑(reshape2)

数据处理主要内容包括&#xff1a;1. 特殊值处理1.1 缺失值1.2 离群值1.3 日期2. 数据转换&#xff08;base vs. dplyr&#xff09;2.1 筛选&#xff08;subset vs. filter/select/rename&#xff09;2.2 排序&#xff08;order vs. arrange&#xff09;2.3 转换&#xff08;tr…

启动linux_使用 UEFI 双启动 Windows 和 Linux | Linux 中国

这是一份在同一台机器上设置 Linux 和 Windows 双重启动的速成解释&#xff0c;使用统一可扩展固件接口&#xff08;UEFI&#xff09;。来源&#xff1a;https://linux.cn/article-12891-1.html作者&#xff1a;Alan Formy-duval译者&#xff1a;郑&#xff08;本文字数&#x…

mysql+odbc+ado_MFC ado+mysql+odbc技术分享

第一步&#xff1a;建立数据库假设有一个sql文件mysql>use dbname; //创建一个数据库名为dbname的数据库(空数据库)mysql>set names utf8; //编码&#xff0c;mysql>source D:/dbname.sql; //导入一个数据库源文件创建数据库内容我做…

js 用下标获取map值_javascript怎么获取map的值?

Map对象保存键/值对&#xff0c;是键/值对的集合。任何值(对象或者原始值) 都可以作为一个键或一个值。Object结构提供了“字符串—值”的对应&#xff0c;Map结构提供了“值—值”的对应。JavaScript获取map值示例&#xff1a;map对象如下&#xff1a;var mapObject {id1001:…