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]);
        }
    }
}

  源碼:這里


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM