select IO複用測試

服務器端測試代碼如下:


int main()
{

    int maxfd,s_ret,i;
    int client_fd;
    int sockfd;//socket返回值
    struct sockaddr_in server_sockaddr,client_sockaddr;
    int sin_size, recvbytes;
    char ipstr[16];
    char cmd[BUFFER_SIZE];
    sin_size=sizeof(struct sockaddr);

    fd_set r_set,listen_set;//定義一個集合

    printf("\t\t****************author by sastar****************\n");
    /*建立socket連接*/
    if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1)
    {
        perror("socket");
        exit(1);
    }
    printf("Socket id = %d\n",sockfd);  

    /*設置sockaddr_in 結構體中相關參數,設置套接字屬性*/
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(PORT);
    //inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr)//單獨設置可接受的ip
    server_sockaddr.sin_addr.s_addr = INADDR_ANY;
    //bzero(&(server_sockaddr.sin_zero), 8);
    memset(&(server_sockaddr.sin_zero),0,8);

    int j = 1;/* 使得重複使用本地地址與套接字進行綁定 */
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j));

    /*綁定函數bind*/
    if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))== -1)//將server_sockaddr結構體的ip和端口號公佈
    {
        perror("bind 1");
        exit(1);
    }
    printf("Bind success!\n");
    /*調用listen函數*/
    if (listen(sockfd, MAX_QUE_CONN_NM) == -1)
    {
        perror("listen");
        exit(1);
    }
    printf("listening...\n");

    FD_ZERO(&r_set);
    FD_SET(sockfd,&r_set);//將socket放入可讀集合中
    maxfd=sockfd+1;//描述符的最大值

    while(1)
    {
        listen_set=r_set;
        s_ret=select(maxfd,&listen_set,NULL,NULL,NULL);
        printf("%d is select detected\n", s_ret);//檢測到的就緒fd數量
        if(s_ret<0)
        {
            perror("select error");
            exit(-1);
        }
        else
        {
            for(i=0;i<maxfd;i++)
            {
                if(FD_ISSET(i,&listen_set))//判斷i是否是在可讀\可寫集合中
                {
                    printf("%d is ready\n", i);//哪個fd就緒
                    if(i==sockfd)
                    {
                        /*調用accept函數,等待客戶端的連接*/
                        if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size)) == -1)//將客戶端傳入的值傳入client_sockaddr,長度爲struct sockaddr
                        {
                            perror("accept");
                            exit(1);
                        }
                        else
                        {
                            inet_ntop(AF_INET,(char *)&client_sockaddr.sin_addr.s_addr,ipstr,16);
                            printf("\nnew client id=%d\nip:%s\nsrc port:%d\n",client_fd,ipstr,client_sockaddr.sin_port);
                            FD_SET(client_fd,&r_set);//將描述符添加到讀寫集合中
                            //FD_SET(client_fd,&write_set);
                            maxfd=(maxfd>(client_fd+1)) ? maxfd : (client_fd+1);
                        }
                    }
                    else
                    {
                        memset(cmd,0,sizeof(cmd));
                        if ((recvbytes = recv(i,cmd,sizeof(cmd),0)) == -1)//接受客戶端發送的命令
                        {
                            perror("recv");
                            close(i);
                            FD_CLR(i,&r_set);//將描述符從讀寫集合中清除
                        }
                        //printf("%s\n", buf);
                        if(strncmp(cmd,"servls",6)==0)
                        {
                            lsfile(i,cmd);
                        }
                        else if(strncmp(cmd,"down ",5)==0)
                        {
                            sendfile(i,cmd);
                        }
                        else if(strncmp(cmd,"up ",3)==0)
                        {
                            recvfile(i,cmd);
                        }
                        else if(strncmp(cmd,"exit",4)==0)
                        {
                            close(i);
                            FD_CLR(i,&r_set);//將描述符從讀寫集合中清除
                        }
                    }
                }
                else{
                    printf("%d is not ready\n", i);//未就緒的是哪些fd
                }
            }
        }
    }
    close(sockfd);
    exit(0);
}

在測試過程中,發現每次客戶端connect時,select總是檢測到fd爲3的文件描述符就緒。如下圖:左側上下兩個終端窗口爲客戶端,右側終端窗口爲服務器端
這裏寫圖片描述
可以看到描述符4/5是新連接的客戶端,那3是什麼呢?每次有新的客戶端連接select就會檢測到。於是通過查看服務器進程的fd(命令:lsof -p <服務器PID號>),如下圖:
這裏寫圖片描述
可以看到,FD那一列中FD0、1、2是系統自帶的stdin、stdout、stderr描述符,FD3(即每次新的客戶端連接會觸發該FD)是LISTEN描述符,端口號是上述代碼中設置的,FD4、FD5是新的客戶端與服務器建立的文件描述符(通過accept函數),以後的客戶端與服務器的數據傳輸都通過FD4/FD5傳輸。所以每次客戶端傳數據到服務器,都是FD4或者FD5被select檢測到,如下圖:
這裏寫圖片描述
可以看到此時打印的消息是“FD5 is ready”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章