zoukankan      html  css  js  c++  java
  • select限制之文件描述符限制

    1、一个进能够打开的最大文件描述符限制。可以通过两种方式修改
    ulimit -n :获取最大文件描述符个数
    ulimit -n 2048:修改为2048个

    该限制的测试代码:

    客户端程序

    /*
    1、select受最大文件描述符限制。测试程序如下
    2、select的fd_set集合容量的限制(FD_SIZE),在头文件中限制。
    */
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<signal.h>
    #include <sys/time.h>
    
    #include <sys/resource.h>
    #define ERR_EXIT(m)
        do
        {
            perror(m);
            exit(EXIT_FAILURE);
        }while(0);
    //为何要使用sleep来推迟ERR_EXIT().  客户端除去 0             、1、2描述符,创建了1021个描述符。在创建1022个套接字时,失败了,失败之后进程退出,发送许多FIN给服务器端。服务器端已完成连接队列中维护了1021个条目。正好有FIN之后,可以空出套接字处理第1021个条目。如果延迟客户端的退出服务器端就只有1020个了。
    int main(void)
    {
        int count=0;
        while(1)
        {    int sock;
            if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
            {
                sleep(5);//推迟客户端的close(便于准确查看服务器端到底能接受多少连接).按理说服务器端只能接受1020个,但是中间有客户端口关闭使得服务端口能够重复利用,使得服务器端也能使用1021个描述符(0,1,2,3(监听套接口)),如果不推迟,创建第1022个套接口时,客户端退出进程,前面的1021个套接口要发送FIN,close和accept交替
                ERR_EXIT("socket");
            }
            struct sockaddr_in servaddr;
            memset(&servaddr,0,sizeof(servaddr));
            servaddr.sin_family=AF_INET;
            servaddr.sin_port=htons(5188);
            servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
            
            if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
                ERR_EXIT("connect");//创建第1022个套接字失败。 0,1,2已经打开。 1024个
            struct sockaddr_in localaddr;
            socklen_t addrlen=sizeof(localaddr);
            if(getsockname(sock,(struct sockaddr *)&localaddr,&addrlen)<0)
                ERR_EXIT("getsockname");
            printf("ip=%s port=%d
    ",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));
            printf("count=%d
    ",++count);
        }
        return 0;
    
    }

    服务器端程序:

    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<signal.h>
    #include<sys/wait.h>
    #define ERR_EXIT(m)
        do
        {
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    ssize_t readn(int fd,void *buf,size_t count)
    {
        size_t nleft=count;
        ssize_t nread;
        char *bufp=(char*)buf;
        while(nleft>0)
        {
            if((nread=read(fd,bufp,nleft))<0)
            {
                if(errno==EINTR)
                    continue;
                else
                    return -1;
            }
            else if(nread==0)
                return (count-nleft);
            bufp+=nread;
            nleft-=nread;
        }
        return count;
    }
    ssize_t writen(int fd, const void *buf, size_t count)
    {
        size_t nleft=count;
        ssize_t nwritten;
        char *bufp=(char*)buf;
        while(nleft>0)
        {
            if((nwritten=write(fd,bufp,nleft))<=0)
            {
                if(errno==EINTR)
                    continue;
                return -1;
            }else if(nwritten==0)
                continue;
            bufp+=nwritten;
            nleft-=nwritten;
        }
        return count;
    
    }
    ssize_t recv_peek(int sockfd,void *buf,size_t len)
    {
        while(1)
        {
            int ret=recv(sockfd,buf,len,MSG_PEEK);//从sockfd读取内容到buf,但不去清空sockfd,偷窥
            if(ret==-1&&errno==EINTR)
                continue;
            return ret;
        }
    }
    //偷窥方案实现readline避免一次读取一个字符
    ssize_t readline(int sockfd,void * buf,size_t maxline)
    {
        int ret;
        int nread;
        size_t nleft=maxline;
        char *bufp=(char*)buf;
        while(1)
        {
            ret=recv_peek(sockfd,bufp,nleft);//不清除sockfd,只是窥看
            if(ret<0)
                return ret;
            else if(ret==0)
                return ret;
            nread=ret;
            int i;
            for(i=0;i<nread;i++)
            {
                if(bufp[i]=='
    ')
                {
                    ret=readn(sockfd,bufp,i+1);//读出sockfd中的一行并且清空
                    if(ret!=i+1)
                        exit(EXIT_FAILURE);
                    return ret;
                }
            }
            if(nread>nleft)
                exit(EXIT_FAILURE);
            nleft-=nread;
            ret=readn(sockfd,bufp,nread);
            if(ret!=nread)
                exit(EXIT_FAILURE);
            bufp+=nread;//移动指针继续窥看
        }
        return -1;
    }
    void handle_sigchld(int sig)
    {
        
        while(waitpid(-1,NULL, WNOHANG)>0)
            ;
            
    }
    void handle_sigpipe(int sig)
    {
        printf("recevie a sig=%d
    ",sig);//打印,不退出服务器进程
    }
    int main(void)
    {    
        int count=0;//测试描述符限制
        signal(SIGCHLD,handle_sigchld);
        signal(SIGPIPE,handle_sigpipe);
        int listenfd;
        if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
            ERR_EXIT("socket error");
        //本地协议地址赋给一个套接字
        struct sockaddr_in servaddr;
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=htons(5188);
        servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//表示本机地址
    
        //开启地址重复使用,关闭服务器再打开不用等待TIME_WAIT
        int on=1;
        if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
            ERR_EXIT("setsockopt error");
        //绑定本地套接字
        if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
            ERR_EXIT("bind error");
        if(listen(listenfd,SOMAXCONN)<0)//设置监听套接字(被动套接字)
            ERR_EXIT("listen error");
        
        struct sockaddr_in peeraddr;//对方套接字地址
        socklen_t peerlen=sizeof(peeraddr);
        
        int client[FD_SETSIZE];//select最大文件描述符,用来保存已连接文件描述符。
        int i=0;    
        for(i=0;i<FD_SETSIZE;i++)
        {
            client[i]=-1;
        }
        int conn;//已连接套接字(主动套接字)
        int nready;
        int maxi=0;//最大不空闲位置
        int maxfd=listenfd;
        fd_set rset,allset;
        FD_ZERO(&rset);
        FD_ZERO(&allset);
        FD_SET(listenfd,&allset);
        while(1)
        {    
            rset=allset;
            nready=select(maxfd+1,&rset,NULL,NULL,NULL);//如果是监听套接口(服务器),已完成连接队列不为空时,accept不再阻塞;
            if(nready==-1)
            {
                if(errno==EINTR)
                    continue;
                ERR_EXIT("select error");
            }
            if(nready==0)
                continue;
            if(FD_ISSET(listenfd,&rset))//监听口有事件,已完成队列不为空
            {
                conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);
                if(conn==-1)
                    ERR_EXIT("accept error");
                for(i=0;i<FD_SETSIZE;i++)
                {
                    if(client[i]<0)
                    {
                        client[i]=conn;
                        if(i>maxi)
                            maxi=i;//更新最大不空闲位置
                        break;
                    }
                    
                }
                if(i==FD_SETSIZE)
                {
                    fprintf(stderr,"too many clents
    ");
                    exit(EXIT_FAILURE);
                }
                printf("ip=%s port=%d
    ",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
                
                printf("%d
    ",++count);//服务器端口应该是1020. 0,1,2和监听的套接口用掉四个
                
                //select的fd_set集合容量的限制
                FD_SET(conn,&allset);//将已连接套接字描述符放入allset,用于监测已连接套接口是否有客户端数据到来
                if(conn>maxfd)
                    maxfd=conn;//更新maxfd
                if(--nready<=0)
                    continue;//如果事件已经处理完,就继续循环监听,不再执行以下代码
                
            }
            for(i=0;i<=maxi;i++)//小于等于
            {
                conn=client[i];
                if(conn==-1)
                    continue;
            //已经连接套接字是否有事件,不用while(1)循环处理客户端发送,有select监听。
                if(FD_ISSET(conn,&rset))
                {
                    int ret;
                    char recvbuf[1024];
                    memset(&recvbuf,0,sizeof(recvbuf));
                    ret=readline(conn,recvbuf,1024);                                                               
                    if(ret==-1)
                        ERR_EXIT("readline");            
                    else if(ret==0)
                    {
                        printf("client close
    ");
                        FD_CLR(conn,&allset);//客户端清理,select就不用去监听
                        client[i]=-1;
                        close(conn);//前面程序BUG,对方关闭之后,我们服务器也要关闭套接口。让客户端接收到通知
                    }
                    fputs(recvbuf,stdout);
                    writen(conn,recvbuf,strlen(recvbuf));//write :aaa  bbb ,RST,写aaa接收bbb,再bbb,有了SIGPIPE
                    if(--nready==0)
                        continue;
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    2016 Multi-University Training Contest 5 ATM Mechine
    2016 Multi-University Training Contest 5 Divide the Sequence
    UVA 10082 WERTYU
    UVA 272 TEX Quotes
    紫书理解
    Codeforces Round #363 (Div. 2) One Bomb
    BestCoder Round #85 hdu5778 abs(素数筛+暴力)
    计算几何模板
    BestCoder Round #85 hdu5777 domino
    BestCoder Round #85 hdu5776 sum
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/11040535.html
Copyright © 2011-2022 走看看