socket编程之select、epoll区别及实现

监视多个socket描述符简单方法-selcet

函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
宏操作fd_set集合:
void FD_CLR(int fd, fd_set *set);清除位图set(描述符集合)中的fd
int FD_ISSET(int fd, fd_set *set);判断fd是否在集合中;
void FD_SET(int fd, fd_set *set);将fd添加到集合中去;
void FD_ZERO(fd_set *set); 将集合set清空成0;

select原理:
1、select同时监视llisten_fd等fd_set集合中的所有描述符,调用select处理后,操作系统将进程加入listen_fd等所有socket描述符的等待队列,进程由运行态转到等待态|
2、只要listen_fd有新数据到达,系统中断唤醒进程,进程由等待态转到运行态(也即将该进程从等待队列移除,加入到运行队列中)
3、进程被唤醒后,进程便知道有数据到达,开始便遍历fd_set集合,若为listen_fd数据,则创建新连接,并将该连接加入集合。若为普通fd数据,则调用recv等操作。

select缺点:
1、轮询方式涉及两次遍历集合:一次为,select被置于死循环中,每调用select都涉及进程等待到运行队列的相互切换,将进程分别加入每个fd描述符的等待队列中。另一次为数据到达select返回时不返回具体是哪个描述符有数据到达,需要遍历集合由FD_ISSET找到具体是哪一个描述符有数据到达。
2、每次遍历都需将整个集合传递给内核,集合越大,速度越慢,代价就越大。
3、出于对效率的考虑,32位linux下默认最大监听数是32x32即1024个,操作系统位数的32倍。可以修改内核头文件#define _FD_SETSIZE 1024的值,但需要重新编译内核,就又涉及到其他一些问题了。注意,此最大描述符限制为单个进程内的限制。

补充说明poll:
poll的原理与select基本类似,只不过select描述符集合是以顺序数组实现,poll是以链表实现。传入时不需要传参数数组,可监听的描述符不局限于1024,可通过cat /proc/sys/fs/file-max查看。缺点是仍然不能指明是哪个socket数据到达。

一个简单的server例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

/*
 * 初始化监听socket描述符
 * */
int init_listen_socket(short port) {
   
    int listen_fd;
    int ret;
    struct sockaddr_in server_addr;

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) {
   
        fprintf(stderr, "fail to socket : %s\n", strerror(errno));
        return -1;
    }

    // 配置listen_fd的TIME_WAIT时可复用
    int on = 1;
    ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    if (ret == -1) {
   
        perror("set sock reuse addr:");
        return -1;
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (ret < 0) {
   
        perror("fail to bind");
        return -1;
    }

    listen(listen_fd, 5);

    return listen_fd;
}

//接受新连接
void chat_loop(int listen_fd)
{
   
	fd_set current, bak;
	int maxfd;
	int ret;
	int i;
	int new_fd;
	char buf[128];
	
	FD_ZERO(&current);
	FD_SET(listen_fd, &current);//将监听描述符加入用户态集合
	maxfd = listen_fd;
	
	while (1) {
   
		bak = current;//内核拷贝
		ret = select(maxfd+1,&bak,NULL,NULL,NULL);
		if(ret < 0) {
   
			perror("select");
			return ;
		}
		
		for (i = 0; i <= maxfd; i++) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值