linux网络编程(2)

(二)多路io复用技术

1.select

2.poll

3.epoll

(1)定义

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

目前epoll是linux大规模并发网络程序中的热门首选模型。

epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

可以使用cat命令查看一个进程可以打开的socket描述符上限。

    cat /proc/sys/fs/file-max

如有需要,可以通过修改配置文件的方式修改该上限值。

    sudo vi /etc/security/limits.conf

    在文件尾部写入以下配置,soft软限制,hard硬限制。如下所示。

    * soft nofile 65536

    * hard nofile 100000

(2)基础API

  1. 创建一个epoll句柄,参数size用来告诉内核监听的文件描述符的个数,跟内存大小有关。

    #include <sys/epoll.h>

    int epoll_create(int size)     size:监听数目

  1. 控制某个epoll监控的文件描述符上的事件:注册、修改、删除。

    #include <sys/epoll.h>

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

        epfd  epoll_creat的句柄

        op    表示动作,用3个宏来表示:

            EPOLL_CTL_ADD (注册新的fdepfd)

            EPOLL_CTL_MOD (修改已经注册的fd的监听事件)

            EPOLL_CTL_DEL (epfd删除一个fd)

        event 告诉内核需要监听的事件

        struct epoll_event {

            __uint32_t events; /* Epoll events */

            epoll_data_t data; /* User data variable */

        };

        typedef union epoll_data {

            void *ptr;

            int fd;

            uint32_t u32;

            uint64_t u64;

        } epoll_data_t;

        EPOLLIN   表示对应的文件描述符可以读(包括对端SOCKET正常关闭)

        EPOLLOUT  表示对应的文件描述符可以写

        EPOLLPRI  表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)

        EPOLLERR  表示对应的文件描述符发生错误

        EPOLLHUP  表示对应的文件描述符被挂断;

        EPOLLET   EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的

        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

  1. 等待所监控文件描述符上有事件的产生,类似于select()调用。

    #include <sys/epoll.h>

    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

        events    用来存内核得到事件的集合,

        maxevents 告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size

        timeout   是超时时间

            -1 阻塞

            0 立即返回,非阻塞

            >0 指定毫秒

        返回值: 成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1

(3)epoll代码实现

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
#include<sys/epoll.h>
int main(int argc, char* argv[]) 
{
	if (argc < 2) 
	{
		printf("too few argument...\n");
		exit(1);
	}
	int port = atoi(argv[1]);

	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	if (lfd == -1) 
	{
		perror("socket");
		exit(1);
	}
	struct sockaddr_in serv_addr;
	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	bind(lfd, (struct sock_addr)&serv_addr, sizeof(serv_addr));

	listen(lfd, 128);
	printf("start to accrpt...\n");

	struct sockaddr_in clit_addr;
	socklen_t cli_len = sizeof(clit_addr);

	int epfd = epoll_create(2000);
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = lfd;
	epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
	struct epoll_event all [2000];
	while (1) 
	{
		int ret = epoll_wait(epfd, all, sizeof(all)/all[0], -1);

		for (int i = 0; i < ret; i++)
		{
			int fd = all[i].data.fd;

			if (fd == lfd)
			{
				int cfd = accept(lfd, (struct sock_addr)&clit_addr; &cli_len);
				if (cfd == -1) 
				{
					perror("accept");
					exit(1);
				}

				struct epoll_event temp;
				temp.events = EPOLLIN;
				temp.data.fd = cfd;
				epoll_ctl(epfd; EPOLL_CTL_ADD, cfd, &temp);
				char ip[16];
				printf("new client IP: %S port: %d\n",
					   inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, ip, 16)
					   ntohs(clit_addr.sin_port));

			}
			else 
			{
				if (!(all[i] & EPOLLIN))
				{
					continue;
				}
				
				char buf[1024] = {0};
				int len = read(fd, buf, sizeof(buf));
				if (len = -1)
				{
					perror("read");
					exit(1);
				}
				else if (len == 0)
				{
					printf("client disconnected...\n");
					if (epoll_ctl(epfd, EPOLL_DEL, fd, NULL) == -1)
					{
						perror("epoll_ctl-del_err");
						exit(1);
					}
					close(fd);
				}
				else
				{
					printf("recv buf:%s\n", buf);
					write(fd, buf, len);
				}
			}
		}
	}
	close(lfd);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值