Linux环境SOCKET编程2:epoll分析
我们通过实现分析知道LT模式下epoll_wait被唤醒可以通过两种方式,而ET模式只能通过一种方式。所以ET模式下能被唤醒的情况,LT模式下一定也能被唤醒。我们先来讨论特殊情况(ET模式),再来讨论一般情况(LT模式)。
1 ET
根据上一节对两种加入rdlist途径的分析,可以得出ET模式下被唤醒(返回就绪)的条件为:
- 对于读取操作:
(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。
(2) 当有新数据到达时,即buffer中的待读内容变多的时候。
另外补充一点:
(3) 当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时(具体见下节内容)。
对于情况(1)(2)分别对应图1(a),图1(b)。
- 对于写操作:
(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。
(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。
另外补充一点:
(3) 当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时(具体见下节内容)。
对于情况(1)(2)分别对应图2(a),图2(b)。
(a) (b)
图1 ET读触发的两种情况
图2 LT写触发的两种情况
2 LT
LT模式下进程被唤醒(描述符就绪)的条件就简单多了,它包含ET模式的所有条件,也就是上述列出的六中读写被唤醒的条件都是用于LT模式。此外,还有更普通的情况LT可以被唤醒,而ET则不理会,这也是我们需要注意的情况。
- 对于读操作
当buffer中有数据,且数据被读出一部分后buffer还不空的时候,即buffer中的内容减少的时候,LT模式返回读就绪。如下图所示。
- 对于写操作
当buffer不满,又写了一部分数据后扔然不满的的时候,即由于写操作的速度大于发送速度造成buffer中的内容增多的时候,LT模式会返回就绪。如下图所示。
注:poll和select都是LT模式。
3 源码分析
服务端:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <poll.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <errno.h>
#include <iostream>
#include <stdio.h>using namespace std;#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 200
#define PORT 8080int setnonblocking(int fd)
{int old_option = fcntl(fd, F_GETFL);int new_option = old_option | O_NONBLOCK;fcntl(fd, F_SETFL, new_option);return old_option;
}void addfd(int epollfd, int fd, bool enable_et)
{epoll_event event;event.data.fd = fd;event.events = EPOLLIN;if (enable_et){event.events |= EPOLLET;}epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);setnonblocking(fd);
}void lt_handle(epoll_event* events, int number, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);addfd(epollfd, connfd, false); } else if (events[i].events & EPOLLIN){printf("event trigger once\n");memset(buf, 0, BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);if (ret <= 0){close(sockfd);continue;}printf("get %d bytes of content: %s\n", ret, buf);}else{printf("something else happened\n");}}
}void et_handle(epoll_event* events, int number, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];char send_buf[BUFFER_SIZE];for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);addfd(epollfd, connfd, false);continue; } else if (events[i].events & EPOLLIN){printf("---------------------------------\n");printf("event trigger once\n");while (1){memset(buf, 0, BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);if (ret <= 0){if (errno == EAGAIN || errno == EWOULDBLOCK){printf("read later\n");break;}close(sockfd);break;}else if (ret == 0){close(sockfd);}else{memset(send_buf, 0, BUFFER_SIZE);sprintf(send_buf, "think, the server have accepted the text, the text is :%s \n", buf);printf("get %d bytes of content: %s\n", ret, buf);}}// 回复客户端消息int nwrite, n,data_size = strlen(send_buf);n = data_size;while (n > 0) {nwrite = write(sockfd, send_buf + data_size - n, n);//ET下一直将要写数据写完if (nwrite < n) {if (nwrite == -1 && errno != EAGAIN) {perror("write error");}break;}n -= nwrite;}close(sockfd);}else {}}
}int main()
{//创建一个监听socketint listenfd = socket(AF_INET,SOCK_STREAM,0);if(listenfd == -1){cout << "create listen socket error" << endl;return -1;}//将监听socker设置为非阻塞的int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if(fcntl(listenfd,F_SETFL,newSocketFlag)==-1) {close(listenfd);cout << "set listenfd to nonblock error" << endl;return -1;}//初始化服务器地址 struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(PORT);if(bind(listenfd, (struct sockaddr*)&bindaddr,sizeof(bindaddr))==-1){cout << "bind listen socker error." << endl;close(listenfd);return -1;}//启动监听if(listen(listenfd,SOMAXCONN)==-1){cout << "listen error." << endl;close(listenfd);return -1;}epoll_event events[MAX_EVENT_NUMBER];//创建epollfdint epollfd = epoll_create(1);if(epollfd == -1){cout << "create epollfd error." << endl;close(listenfd);return -1;}addfd(epollfd, listenfd, true);while(true){ int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);if(ret < 0){//被信号中断if(errno == EINTR) continue;//出错,退出printf("epoll failed\n");break;}//lt_handle(events, ret, epollfd, listenfd);et_handle(events, ret, epollfd, listenfd); }close(listenfd);return 0;
}
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>#define MYPORT 8080
#define BUFFER_SIZE 1024int main()
{///定义sockfdint sock_cli = socket(AF_INET,SOCK_STREAM, 0);///定义sockaddr_instruct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(MYPORT); ///服务器端口servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ///服务器ip///连接服务器,成功返回0,错误返回-1if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){perror("connect");exit(1);}char sendbuf[BUFFER_SIZE];char recvbuf[BUFFER_SIZE];while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL){send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送if(strcmp(sendbuf,"exit\n")==0)break;recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收fputs(recvbuf, stdout);memset(sendbuf, 0, sizeof(sendbuf));memset(recvbuf, 0, sizeof(recvbuf));}close(sock_cli);return 0;
}
Linux环境SOCKET编程2:epoll分析相关推荐
- Linux环境SOCKET编程3:压力测试
测试方法:使用epoll实现一个通用的服务器压力测试程序. 测试代码: #include <stdlib.h> #include <stdio.h> #include < ...
- linux下socket编程和epoll的使用
这两天在学Linux下的网络编程,于是便看了些关于socket和epoll的资料. 首先介绍socket,socket编程我之前也接触过,不过是在windows下接触的.和windows不同的 ...
- Linux环境SOCKET编程5:定时器接口timerfd
1.概述 timerfd是Linux为用户程序提供的一个定时器接口.这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景.timerfd是lin ...
- Linux环境SOCKET编程1:套接字
一.socket运行过程 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.在这时如果有个客户端初始化一个Socket,然 ...
- Linux下Socket编程
Linux下Socket编程 网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...
- Linux的SOCKET编程 简单演示
转载:http://blog.csdn.net/hguisu/article/details/7445768/ Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源 ...
- asp.core api 通过socket和服务器通信发送udp_详解Linux的SOCKET编程
文章来自于 https://www.zhangshengrong.com/p/9Oabd95XdK/ PHP进阶学习交流QQ群:983229225 本篇文章对Linux的SOCKET编程进行了详细解释 ...
- LINUX下Socket编程 函数格式详解
你需要了解的一些系统调用: socket() bind() connect() listen() accept() send() recv() sendto() recvfrom() close() ...
- Linux的SOCKET编程详解
Linux的SOCKET编程详解 一. 网络中进程之间如何通信 进程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统 ...
最新文章
- SQL查询 的一些原则
- 手持GPS坐标系统的转换与应用
- [转]通达信主图指标--13日黄金K线
- win10家庭版无法安装mysql_大师处置win10系统家庭版安装MySQL server 5.7.19失败的详细办法...
- android httpget 参数,如何在Android中向HTTP GET请求添加参数?
- shell 获取指定ip的丢包率
- MT2503环境搭建步骤及注意事项
- C++实现Vgg19分类器(四)主函数
- 产品经理之如何做好演讲
- 形式化验证(Formal verification)如何确保完美的智能合同?
- GIS招聘 | 青海省省直事业单位(含地信、测绘等专业)
- 组播域GNS3与eNSP桥接以及GNS3的使用教程
- 黑马粉丝感叹:好可呀,好想要!!【最新福利你还没领?】
- 基于JavaScript+css写一个简单的h5动态下雨效果
- KEIL MDK中的RO、RW和ZI
- java 图像合成加相框_合成走进相框人物照片效果的PS教程
- 计算机与计算思维读后感论文,读书笔记——计算思维培养的一点领悟与构想
- 无人值守安装 linux 系统
- 全景图有什么优势?为什么这么多人在做?
- Harbor仓库清理