• Server Develop (四) select实现非阻塞sever


    Select server

      linux 的socket函数分为阻塞和非阻塞两种方式,比如accept函数,在阻塞模式下,它会一直等待有客户连接。而在非阻塞情况下,会立刻返回。我们一般都希望程序能够运行在非阻塞模式下。一种方法就是做一个死循环,不断去查询各个socket的状态,但是这样会浪费大量的cpu时间。解决这个问题的一个方法就是使用select函数。使用select函数可以以非阻塞的方式和多个socket通信。当有socket需要处理时,select函数立刻返回,期间并不会占用cpu时间。 

      select函数原型:

    //Select 函数原型
     int select(nfds, readfds, writefds, exceptfds, timeout)
     //nfds: select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要  监视各文件中的最大文件号加一
     //readfds:select监视的可读文件句柄集合
     //writefds:select监视的可写文件句柄集合。
     //exceptfds:select监视的异常文件句柄集合。
     //timeout:本次select的超时结束时间。

       当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用一组系统提供的宏在select()结束时便可判 断哪一文件可读或可写。对Socket编程特别有用的就是readfds.

      select相关函数:

    FD_ZERO(fd_set *fdset)
    //清空fdset与所有文件句柄的联系。
     
    FD_SET(int fd, fd_set *fdset)
    //建立文件句柄fd与fdset的联系。
     
    FD_CLR(int fd, fd_set *fdset)
    //清除文件句柄fd与fdset的联系。
     
    FD_ISSET(int fd, fdset *fdset)
    //检查fdset联系的文件句柄fd是否可读写,>0表示可读写。
     

      具体步骤: 

    //大致如下:……
     
    int sockfd;
    fd_set fdR;
    int sock_fd[N];//save client_sockfd, size is N
    struct timeval timeout;   while(1){ FD_ZERO(&fdR); FD_SET(sockfd, &fdR); switchselect(sockfd + 1,&fdR, NULL, &timeout)) {   case -1:error;   case 0:timeout;   default:       if (FD_ISSET(sockfd, &fdR)) {      now read or recv something;       } }   /* if sock_serv is in &fdR, now accept() */   if( FD_ISSET(sock_serv,&fdR) ){    sock_fd[..] = now accept(); } }

      具体代码:

    void select_server::starListen()
    {
        int flag = listen(sock_serv, 5);
        if(flag < 0){
            perror("listen error!\n");
            close(sock_serv);
            return;
        }
        printf("listen!\n");
        fd_set client_fdset;    //监控文件描述符集合
        int maxsock;            //监控文件描述符中最大的文件号
        struct timeval tv;        //超时返回时间
        int client_sockfd[5];   //存放活动的sockfd
        bzero((void*)client_sockfd,sizeof(client_sockfd));
        int conn_amount = 0;    //用来记录描述符数量
        maxsock = sock_serv;
    
        char buffer[1024];
    
        while(1){
    
            //初始化文件描述符号到集合
            FD_ZERO(&client_fdset);
            //加入服务器描述符
            FD_SET(sock_serv,&client_fdset);
    
            //设置超时时间
            tv.tv_sec = 30; //30秒
            tv.tv_usec = 0;
    
            //把活动的句柄加入到文件描述符中
            for(int i = 0; i < 5; ++i){//程序中Listen中参数设为5,故i必须小于5
                if(client_sockfd[i] != 0){
                    FD_SET(client_sockfd[i], &client_fdset);
                }
            }
            //printf("put sockfd in fdset!\n");
            //select函数
            flag = select(maxsock+1, &client_fdset, NULL, NULL, &tv);
            if(flag < 0){
                perror("select error!\n");
                break;
            }
            else if(flag == 0){
                printf("timeout!\n");
                continue;
            }
    
            //轮询各个文件描述符
            for(int i = 0; i < conn_amount; ++i){
                //FD_ISSET检查client_sockfd是否可读写,>0可读写
                if(FD_ISSET(client_sockfd[i], &client_fdset)){
                    printf("start recv from client[%d]:\n",i);
                    flag = recv(client_sockfd[i], buffer, 1024, 0);
                    if(flag <= 0){
                        printf("client[%d] close\n", i);
                        close(client_sockfd[i]);
                        FD_CLR(client_sockfd[i], &client_fdset);
                        client_sockfd[i] = 0;
                    }
                    else{
                        printf("recv from client[%d] :%s\n", i, buffer);
                    }
                }
            }
    
            //检查是否有新的连接,如果收,接收连接,加入到client_sockfd中
            if(FD_ISSET(sock_serv, &client_fdset)){
                //接受连接
                struct sockaddr_in client_addr;
                size_t size = sizeof(struct sockaddr_in);
                int sock_client = accept(sock_serv, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));
                if(sock_client < 0){
                    perror("accept error!\n");
                    continue;
                }
                //把连接加入到文件描述符集合中
                if(conn_amount < 5){
                    client_sockfd[conn_amount++] = sock_client;
    
                    bzero(buffer,1024);
                    strcpy(buffer, "this is server! welcome!\n");
                    send(sock_client, buffer, 1024, 0);
    
                    printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    
                    bzero(buffer,sizeof(buffer));
                    flag = recv(sock_client, buffer, 1024, 0);
                    if(flag < 0){
                        perror("recv error!\n");
                        close(sock_serv);
                        return;
                    }
                    printf("recv : %s\n",buffer);
    
                    if(sock_client > maxsock){
                        maxsock = sock_client;
                    }
                    else{
                        printf("max connections!!!quit!!\n");
                        break;
                    }
                }
            }
        }
    
        for(int i = 0; i < 5; ++i){
            if(client_sockfd[i] != 0){
                close(client_sockfd[i]);
            }
        }
    }

      源码:这里

  • 相关阅读:
    Jquery事件
    基础:装箱和拆箱...
    navicat编辑表的作用
    谷歌浏览器preview展示问题
    @Scheduled并行执行
    spring异步执行方法线程池的配置
    dubbo的ExceptionFilter异常处理
    dubbo异常处理
    idea设置启动jvm参数
    前后端分离走本地代码Charles的使用
  • 原文地址:https://www.cnblogs.com/coder2012/p/3138530.html
Copyright © 2020-2023  润新知