網絡編程select的使用.


記錄下簡單的select的使用。以防忘記。

服務端代碼

#include <stdio.h>
#include "unp.h"
int main() {
    int i,maxi,maxfd,listenfd,clifd;
    int readycnt,client[FD_SETSIZE];
    int n;
    char buff[MAXLINE];
    //這里rset是select的監聽列表。但是因為每次循環完都要重新加入監聽列表。所以我們把要監聽的數據放到allset。然后把allset賦值給rset
    fd_set rset,allset;
    //服務端套接口創建,綁定,啟動監聽
    struct sockaddr_in serAddr;
    bzero(&serAddr,sizeof(serAddr));
    serAddr.sin_family=AF_INET;
    serAddr.sin_port=htons(SERV_PORT);
    serAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    listenfd=Socket(AF_INET,SOCK_STREAM,0);
    socklen_t len=sizeof(serAddr);
    Bind(listenfd,(SA *)&serAddr,len);
    Listen(listenfd,LISTENQ);
    //初始化select要監聽的最大文件描述符id,現在還沒有接收客戶端,所以最大肯定是服務端監聽端口
    maxfd=listenfd;
    maxi=-1;
    //客戶端的連接數不能超過FD_SETSIZE。所有套接口描述符都將裝在client數組里面。默認沒有填充的值為-1.這里初始化一下
    for(i=0;i<FD_SETSIZE;i++){
        client[i]=-1;
    }
    FD_ZERO(&allset);             //初始化監聽列表
    FD_SET(listenfd,&allset);     //將服務端端口監聽的套接口放入select的監聽列表
    for(;;){
        rset=allset;              //所有要監聽和取消監聽都是操作allset。然后開始的時候把allset賦值給rset
        readycnt=Select(maxfd+1,&rset,NULL,NULL,NULL);  //開始監聽
        if(FD_ISSET(listenfd,&rset)){                   //如果是服務端端口的套接口收到消息,說明是有客戶端連接上來
            clifd=Accept(listenfd,(SA *)NULL,NULL);     //接收客戶端連接
            for(i=0;i<FD_SETSIZE;i++){                  //遍歷存放客戶端連接套接口的數組。找到一個-1的就是可以存放的位置
                if(client[i]==-1)
                    break;
            }
            if(i==FD_SETSIZE)                           //如果找到結束都沒有。則說明所有隊列滿了
                err_quit("too many clients");

            client[i]=clifd;                //記錄客戶端套接口
            FD_SET(clifd,&allset);          //加入select的監聽隊列
            if(clifd>maxfd)                 //更新最大文件描述符
                maxfd=clifd;
            if(i>maxi)                      //更新最高的位置信息。可以讓后面不用完整遍歷
                maxi=i;
            printf("cli connect success maxi:%d \r\n",maxi);
            if(--readycnt<=0)               //這個就緒套接口處理完成,遞減一下。如果已經沒其他的了。就不用繼續往后遍歷了
                    break;
            }
        }
    }
}

客戶端例子

#include <stdio.h>
#include "unp.h"

void send_str(int sockfd,int stdinfd){
    fd_set rset;              //監聽的列表
    FD_ZERO(&rset);           //重置
    int stdinof=0;            //標記標准輸入是否關閉了
    int n;
    char buff[MAXLINE];
    for(;;){
        if(stdinof==0)        //標准輸入如果關閉了就不要監聽了
            FD_SET(stdinfd,&rset);
        FD_SET(sockfd,&rset);       //監聽套接口
        //select第一個參數是要用最大文件描述符+1.我們監聽的是標准輸入和套接口。標准輸入輸出是0和1。套接口描述符肯定要大。所以直接填sockfd+1
        Select(sockfd+1,&rset,NULL,NULL,NULL); //阻塞,等待通信管道准備就緒
        if(FD_ISSET(stdinfd,&rset)){                    //判斷是否是io管道就緒了
            if((n=Read(stdinfd,buff,MAXLINE))==0){      //接收輸入結果,如果結果是0,就代表管道關閉了
                stdinof=1;              //設置后上面不再監聽
                Shutdown(sockfd,SHUT_WR);               //半關閉寫入接口,服務端就會read到0.然后關閉和客戶端的連接
                FD_CLR(stdinfd,&rset);                  //從監聽列表移除
                continue;
            }
            Writen(sockfd,buff,n);                      //正常情況就把標准輸入的發送給服務端
        }
        if(FD_ISSET(sockfd,&rset)){     //如果是服務端的套接口有數據了
            if((n=Read(sockfd,buff,MAXLINE))==0){       //讀取服務端返回數據
                printf("sock read=0\r\n");
                if(stdinof==1)                          //如果標准輸入已經關掉了,就不用再往下走了
                    return;
                else
                    err_quit("ser close\r\n");          //其他服務端關掉的情況打印消息並退出
            }
            Write(STDOUT_FILENO,buff,n);                //正常情況就寫入到標准輸出
        }
    }
}

int main(int argc,char** argv) {
    if(argc!=2)
        err_quit("argc err");
    char *addr=argv[1];
    //鏈接服務端
    struct sockaddr_in serAddr;
    serAddr.sin_family=AF_INET;
    serAddr.sin_port=htons(SERV_PORT);
    Inet_pton(AF_INET,addr,&serAddr.sin_addr);
    int sockfd=Socket(AF_INET,SOCK_STREAM,0);
    Connect(sockfd,(SA*)&serAddr,sizeof(serAddr));
    //鏈接成功后的業務邏輯
    send_str(sockfd,STDIN_FILENO);
    exit(0);
}

select還有兩點最容易出錯的地方,

1、是忘記對最大描述字+1。也就是select的第一個參數經常會出錯

2、忘記描述字集是值-結果參數。也就是rset里面是fd-結果參數。所以rset總是要重置來再次監聽。因為之前設置的1又變回0了。


免責聲明!

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



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