Linux-IO复用,select

1 篇文章 0 订阅
1 篇文章 0 订阅

select将IO扩充复用,一个IO监听多个文件,当至少有一个文件有事件时select才会返回。但还是受句柄限制(fd句柄索引文件,走虚拟文件系统,监听文件的输入输出,文件多时会很耗时),和poll基本一致
select虽然能实现并发,但逃离不了句柄数量的限制,epoll可以解决(将文件转化为event)。所谓集群就是多epoll,做QOS负载均衡
select后fd_set会被更改,所以每次都要清空fd_set再添加
在这里插入图片描述
这里将标准输入fd和新连接fd放入fd_set。tv设置超时时间
fd放入fd_set前最好设置成非阻塞(默认是非阻塞)
select之后数据并未发送,还需要遍历fd后,read\write才发送

实现方法二:
将监听句柄也放入文件集,一有新链接就把新链接放入select,实现多链接通信,增加fd_set容量的利用效率

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

#define _BACKLOG_ 5 //监听队列里允许等待的最大值

int fds[20];//用来存放需要处理的IO事件

int creat_sock(char *ip,char *port)//集成bind和listen的方法
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0){
        perror("creat_sock error");
        exit(1);
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(port));
    local.sin_addr.s_addr = inet_addr(ip);
    
    if( bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){
        perror("bind");
        exit(2);
     }

    if(listen(sock,_BACKLOG_) < 0 ){
        perror("listen");
        exit(4);
     }

    return sock;
}

int main(int argc,char* argv[])
{
    if(argc != 3){
        printf("Please use : %s [ip] [port]\n",argv[0]);
        exit(3);
    }
    int listen_sock = creat_sock(argv[1],argv[2]);

    size_t fds_num = sizeof(fds)/sizeof(fds[0]);//清空数组
    size_t i = 0;
    for(;i < fds_num;++i)
    {
        fds[i] = -1;//只要是-1的位置就有空位
    }
    
    int max_fd = listen_sock;//最开始fd最大值是listen_sock
	
    fd_set rset;
    while(1)
	{
        FD_ZERO(&rset);
        FD_SET(listen_sock,&rset);
		max_fd = listen_sock;
        //struct timeval timeout = {20 , 0};//超时时间20s
		fds[0]=listen_sock;
        size_t i = 0;
        for(i=1;i < fds_num;++i)
        {
            if(fds[i] > 0 ){//只要大于0,就有fd
                FD_SET(fds[i] ,&rset);
                if(max_fd < fds[i])//冒泡得到最大值
				{
                    max_fd = fds[i];
                }
            }
        }

        switch(select(max_fd+1,&rset,NULL,NULL,NULL))//内部实现需要值最大的fd+1来确定fd边界,最后一个参数是超时时间结构体,不设置超时就为NULL
        {
            case -1://错误
                perror("select");
                break;
            case 0://超时
                printf("time out..\n");
				break;
            default://有数据
            {
                size_t i = 0;
                for(;i < fds_num;++i)
                {
                 //当为listen_socket事件就绪的时候,就表明有新的连接请求
                    if(FD_ISSET(fds[i],&rset) && fds[i] == listen_sock)
                    {
                        struct sockaddr_in client;
                        clilen=sizeof(client);
                        //返回一个链接句柄,不会阻塞
                        int accept_sock = accept(listen_sock,(struct sockaddr*)&client,&clilen);//原源码在这里出错,没有&clilen而是直接sizeof(client)
                        if(accept_sock < 0){
                            perror("accept");
                            exit(5);
                        }
						char * paddr=NULL;
						char * saddr=NULL;
						paddr=inet_ntoa(client.sin_addr);
						saddr=inet_ntoa(client.sin_addr);
                        printf("connect by a client, ip:%s port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

                        size_t i = 0;
                        for(;i < fds_num;++i)//将新接受的描述符(链接句柄)存入集合中
                        {
                            if(fds[i] == -1){
                                fds[i] = accept_sock;
                                break;
                            }
                        }
                        if(i == fds_num)//如果没空位就关闭此链接句柄(可设置数量最多1000个)
                        {
                            close(accept_sock);
                        }
                    }
                    //普通请求
                    else if(FD_ISSET(fds[i],&rset) && (fds[i] > 0))
                    {
                        char buf[1024];
                        memset(buf,'\0',sizeof(buf));
                        ssize_t size = read(fds[i],buf,sizeof(buf)-1);//接收链接句柄发来的数据
                        if(size < 0){
                            perror("read");
                            exit(6);
                        }else if(size == 0){
                            printf("client close..\n");
                            close(fds[i]);
                            fds[i] = -1;
                        }else{
                            printf("client say: %s\n",buf);
                        }
                        
                    }
                    else{}
                }
                
            }
            break;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值