socket epoll通讯

7 篇文章 0 订阅
2 篇文章 0 订阅

     ~~~~     说道epoll就要说道和select的比较了,这是比较常见的面试题目了。
     ~~~~     先介绍epoll:epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
     ~~~~     epoll获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
     ~~~~     epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
     ~~~~     epoll只提供了三个函数epoll_create, epoll_ctl和epoll_wait。
     ~~~~     两个模式

LT(level triggered)是epoll缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
ET (edge-triggered)是高速工作方式,只支持no-block socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。

     ~~~~     因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

下面记录下epoll和select的差异。
     ~~~~     epoll监视的描述符数量不受限制,他所支持的FD上限是最大可以打开的文件的数目,这个数字远远大于2048,具体可以cat /proc/sys/fs/file-max查看,这个数目和系统内存有关。seletc的fd数量有限制,一般大的服务器都不能满足。
     ~~~~     epoll的IO效率不会随着监视fd的数量增长而下降,epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现,只有就绪的fd才是执行回调函数
     ~~~~     支持两种工作方式,具体看上面介绍
     ~~~~     mmap加速内核与用户空间的信息传递。epoll是通过内核于用户空间mmap同一块内存,避免了的内存拷贝,而select需要对数据进行拷贝。

直接上源码,epoll,实现服务端,使用NetAssist工具测试

server

#include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <vector>
#include<algorithm>

#define MAXBUFFSIZE 1024
#define MAXEVENTS 500
#define FDSIZE 1000

const int port_server = 9095;
const char* ip_address = "192.168.1.57";

char buf[MAXBUFFSIZE] = { 0 };
int buflen = 0;
std::vector<int> cli_fds;

void handle_events(struct epoll_event *events, int num, int socketfd, int epoll_fd) {
	int i;
	int fd;
	//进行选好遍历
	for (i = 0; i < num; i++) {
		fd = events[i].data.fd;
		//根据描述符的类型和事件类型进行处理
		if ((fd == socketfd) && (events[i].events& EPOLLIN)) {
			int cli_fd;
			struct sockaddr_in cli_addr;
			socklen_t cliaddrlen = sizeof(cli_addr);
			cli_fd = accept(socketfd, (struct sockaddr*)&cli_addr, &cliaddrlen);
			if (cli_fd == -1) {
				fprintf(stderr, "client socket accpet failed! errno:%s.\r\n", strerror(errno));
				return ;
			}
			else {
				printf("accept a new client:%s:%d \r\n", inet_ntoa(cli_addr.sin_addr), cli_addr.sin_port);

				struct epoll_event ev;
				ev.events = EPOLLIN;
				ev.data.fd = cli_fd;
				epoll_ctl(epoll_fd, EPOLL_CTL_ADD, cli_fd, &ev);
				cli_fds.push_back(cli_fd);
				return;
			}
		}
		else if (events[i].events & EPOLLIN){
			if (fd == STDIN_FILENO) {
				bzero(buf, MAXBUFFSIZE);
				// 先获取输入数据
				fgets(buf, MAXBUFFSIZE, stdin);
				if (strlen(buf) > 0){
					// 发送
					fprintf(stdout, "send msg:%s.\r\n", buf);

					// 轮询发送给所有客户端
					std::vector<int>::iterator it = cli_fds.begin();
					while (it != cli_fds.end()) {
						send(*it, buf, strlen(buf), 0);
						it++;
					}
				}
				return;
			}
			else {
				bzero(buf, MAXBUFFSIZE);
				int ret_len = static_cast<int>(read(fd, buf, MAXBUFFSIZE));
				if (ret_len == -1) {
					fprintf(stderr, "read failed! errno:%s.\r\n", strerror(errno));
					close(fd);

					struct epoll_event ev;
					ev.events = EPOLLIN;
					ev.data.fd = fd;
					epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
					std::vector<int>::iterator it = find(cli_fds.begin(), cli_fds.end(), fd);
					if (it != cli_fds.end()) {
						cli_fds.erase(it);
					}
					else {
						fprintf(stderr, "vector remove %d failed! errno:%s.\r\n", fd, strerror(errno));
					}
					return;
				}
				else if (ret_len == 0) {
					fprintf(stdout, "client socket close! errno:%s.\r\n", strerror(errno));
					close(fd);

					struct epoll_event ev;
					ev.events = EPOLLIN;
					ev.data.fd = fd;
					epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
					std::vector<int>::iterator it = find(cli_fds.begin(), cli_fds.end(), fd);
					if (it != cli_fds.end()) {
						cli_fds.erase(it);
					}
					else {
						fprintf(stderr, "vector remove %d failed! errno:%s.\r\n", fd, strerror(errno));
					}
					return;
				}
				else {
					struct sockaddr_in cli_addr;
					socklen_t cliaddrlen = sizeof(cli_addr);
					getpeername(fd, (struct sockaddr*)&cli_addr, &cliaddrlen);
					fprintf(stdout, "client:%s,msg:%s.\r\n", inet_ntoa(cli_addr.sin_addr), buf);

					//struct epoll_event ev;
					//ev.events = EPOLLOUT;
					//ev.data.fd = fd;
					//epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
				}
			}
		}
		//else if (events[i].events & EPOLLOUT){
		//	//写
		//	//int wr_len = write(fd, buf, buflen);
		//	fprintf(stdout, "EPOLLOUT msg:%s.\r\n", buf);
		//}
		else {
			close(fd);
		}
	}
}


int main(){
	int srv_fd = 0;
	struct sockaddr_in srv_addr;
	int tmp = 0;

	srv_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (srv_fd < 0) {
		fprintf(stderr, "socket create failed! errno:%s.\r\n", strerror(errno));
		return -1;
	}

	//srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 让系统自己获取本机ip
	inet_pton(AF_INET, ip_address, &srv_addr.sin_addr);
	srv_addr.sin_port = htons(port_server);
	srv_addr.sin_family = AF_INET;

	// 非阻塞
	int flags = fcntl(srv_fd, F_GETFL, 0);
	fcntl(srv_fd, F_SETFL, flags | O_NONBLOCK);

	// 地址重用
	tmp = 1;
	if (setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) == -1) {
		close(srv_fd);
		fprintf(stderr, "setsockopt set SO_REUSEADDR failed. errno:%s.\r\n", strerror(errno));
		return -1;
	}

	if (bind(srv_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)) == -1) {
		close(srv_fd);
		fprintf(stderr, "bind socket failed. errno:%s.\r\n", strerror(errno));
		return -1;
	}

	if (listen(srv_fd, 5) == -1) {
		close(srv_fd);
		fprintf(stderr, "listen socket failed. errno:%s.\r\n", strerror(errno));
		return -1;
	}

	printf("create server socket success! \r\n start server. \r\n");

	struct epoll_event e_events[MAXEVENTS];
	int epoll_fd = 0;

	if ((epoll_fd = epoll_create(FDSIZE)) == -1) {
		close(srv_fd);
		fprintf(stderr, "epoll create failed. errno:%s.\r\n", strerror(errno));
		return -1;
	}

	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = srv_fd;
	epoll_ctl(epoll_fd, EPOLL_CTL_ADD, srv_fd, &ev);

	//输入监测
	ev.events = EPOLLIN | EPOLLET;
	ev.data.fd = STDIN_FILENO;
	epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);

	while (1) {
		tmp = epoll_wait(epoll_fd, e_events, MAXEVENTS, -1);
		handle_events(e_events, tmp, srv_fd, epoll_fd);
	}

	close(epoll_fd);
	close(srv_fd);
	return 0;
}

client

#include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <vector>
#include<algorithm>

#define MAXBUFFSIZE 1024
#define MAXEVENTS 10
#define FDSIZE 10

const int port_server = 9095;
const char* ip_address = "192.168.1.57";

char buf[MAXBUFFSIZE] = { 0 };
int buflen = 0;

bool handle_events(struct epoll_event *events, int num, int socketfd, int epoll_fd) {
	int i;
	int fd;
	//进行选好遍历
	for (i = 0; i < num; i++) {
		fd = events[i].data.fd;
		//根据描述符的类型和事件类型进行处理
		if ((fd == socketfd) && (events[i].events& EPOLLIN)) {
			// 接收数据
			bzero(buf, MAXBUFFSIZE);
			int ret = static_cast<int>(read(fd, buf, MAXBUFFSIZE));
			if (ret <= -1) {
				fprintf(stderr, "read failed! errno:%s.\r\n", strerror(errno));
				close(fd);
				struct epoll_event ev;
				ev.events = EPOLLIN;
				ev.data.fd = fd;
				epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
				return false;

			}
			else {
				fprintf(stdout, "revc msg:%s.\r\n", buf);
			}
		}
		else if (fd == STDIN_FILENO && events[i].events & EPOLLIN) {
			if (fd == STDIN_FILENO) {
				bzero(buf, MAXBUFFSIZE);
				// 先获取输入数据
				fgets(buf, MAXBUFFSIZE, stdin);
				if (strlen(buf) > 0) {
					// 发送
					fprintf(stdout, "send msg:%s.\r\n", buf);
					send(socketfd, buf, strlen(buf), 0);
				}
			}
		}
		else {
			fprintf(stderr, "line:%d.socket failed! errno:%s.\r\n", __LINE__, strerror(errno));
			close(fd);
			return false;
		}
	}
	return true;
}


int main() {
	int cli_fd = 0;
	struct sockaddr_in srv_addr;
	int tmp = 0;

	cli_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (cli_fd < 0) {
		fprintf(stderr, "socket create failed! errno:%s.\r\n", strerror(errno));
		return -1;
	}

	bzero(&srv_addr, sizeof(srv_addr));
	inet_pton(AF_INET, ip_address, &srv_addr.sin_addr);
	srv_addr.sin_port = htons(port_server);
	srv_addr.sin_family = AF_INET;

	int ret = connect(cli_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if (ret < 0) {
		fprintf(stderr, "connect server failed! errno:%s.\r\n", strerror(errno));
		close(cli_fd);
		return -1;
	}

	printf("connect server success! \r\n start server. \r\n");

	struct epoll_event e_events[MAXEVENTS];
	int epoll_fd = 0;

	if ((epoll_fd = epoll_create(FDSIZE)) == -1) {
		close(cli_fd);
		fprintf(stderr, "epoll create failed. errno:%s.\r\n", strerror(errno));
		return -1;
	}

	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = cli_fd;
	epoll_ctl(epoll_fd, EPOLL_CTL_ADD, cli_fd, &ev);

	//输入监测
	ev.events = EPOLLIN | EPOLLET;
	ev.data.fd = STDIN_FILENO;
	epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);

	while (1) {
		tmp = epoll_wait(epoll_fd, e_events, MAXEVENTS, -1);
		if (!handle_events(e_events, tmp, cli_fd, epoll_fd)) {
			break;
		}
		
	}

	close(epoll_fd);
	close(cli_fd);
	return 0;
}

复制错代码了,今天自己看的时候才发现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值