1. 程式人生 > >select 伺服器 客戶端 縮水版本

select 伺服器 客戶端 縮水版本

 

 

tcpserver.c

int main(int argc, char**argv)
{
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in serv_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    memset(&serv_addr,0,sizeof(serv_addr));
    memset(&client_addr,0,sizeof(client_addr));
    
    serv_addr.sin_addr.s_addr = INADDR_ANY; //繫結所有ip
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port = htons(PORT);
    int opt = 1;
    socklen_t  optlen = sizeof(opt);
    
    //設定複用ip
    if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,optlen) < 0){
        perror("setsockopt");
        return  0;
    }

    //設定ip port
    if(bind(listenfd,(SA*)&serv_addr,sizeof(serv_addr)) < 0){
        perror("bind");
        return 0;
    }
    
    //BACKLOG = 10
    if(listen(listenfd,BACKLOG) < 0){
        perror("listen");
        return 0;
    }
    
    //一些變數,下面會用到
    int nready = 0,client[FD_SETSIZE] , maxfd = listenfd , connfd = 0,maxi = -1;
    
    // client 用於儲存 客戶描述符
    for(int i = 0; i < FD_SETSIZE ; ++i)
        client[i] = -1;
    fd_set rset ,allset;
    FD_ZERO(&allset);
    
    //把監聽套接字先置位
    FD_SET(listenfd,&allset);
    int clientfd = -1 , n = 0 , i = 0;
    char buf[BUFSIZ];
    while(1){
        //select 每次將修改rset,需要重置
        rset = allset;
        nready = select(maxfd+1,&rset,NULL,NULL,NULL);
        printf("nread : %d \n" , nready);
        
        //可能被訊號打斷
        if(nready < 0){
            perror("select");
            continue;
        }
        //客戶連結 進來
        if(FD_ISSET(listenfd,&rset)){
            client_len = sizeof(client_addr);
            connfd = accept(listenfd,(SA*)&client_addr,&client_len);
            printf("a client : %d\n" , connfd);
            
            //找個位置放進去
            for(i = 0; i < FD_SETSIZE; ++i){
                if(client[i] < 0) {
                    client[i] = connfd;
                    break;
                }
            }
            //伺服器已滿
            if(FD_SETSIZE == i){
                close(connfd);
                puts("server is full");
            }
            else {
                
                //把客戶fd 放入監聽集合中
                FD_SET(connfd,&allset);
                if(connfd > maxfd)
                    maxfd = connfd;
                //client 索引
                if( i > maxi)
                    maxi = i;
                //如果數量為0 則不需要往下繼續了
                if(--nready == 0)
                    continue;
            }
        }
        
        for(int i = 0 ; i <= maxi;++i){
            
            if((clientfd = client[i]) <0 )
                continue;
            
            //直到找到一個可讀的fd
            if(FD_ISSET(clientfd,&rset)){
                printf("clientfd : %d is ready\n", clientfd);
                
                //如果對斷關閉了 
                if((n = read(clientfd,buf,BUFSIZ)) <= 0){
                    printf("clientfd : %d closed\n",clientfd);
                    //清空當前fd 所存在的地方
                    close(clientfd);
                    FD_CLR(clientfd,&allset);
                    client[i] = -1;
                } else{
                    write(clientfd,buf,n);
                }
                if(--nready == 0)
                    break;
            }
        }
    }


    return 0;
}

 

tcpclient.c



void str_echo(int sockfd);
int max(int a, int b){
    return a > b ? a : b;
}
int main(int argc, char**argv)
{
    if(argc != 2){
        puts("ip addr");
        return 0;
    }

    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in sin;
    memset(&sin,0,sizeof(sin));
    sin.sin_port = htons(PORT);
    sin.sin_family = AF_INET;

    //把字串轉成網路位元組序
    inet_pton(AF_INET,argv[1],&sin.sin_addr);

    connect(sockfd,(SA*)&sin,sizeof(sin));
    str_echo(sockfd);

    return 0;
}

void str_echo(int sockfd){
    int maxfd = sockfd;
    int eof = 0 , readn = 0 , fd_num = 0, n= 0;
    char buf[BUFSIZ];
    fd_set rset;
    FD_ZERO(&rset);

    while(1)
    {
        //EOF ==》 CTRL+D
        if(0 == eof){
            FD_SET(0,&rset);
        }

        //select 將修改rset ,每次重置
        FD_SET(sockfd,&rset);
        maxfd = max(0,sockfd);
        fd_num = select(maxfd+1,&rset,NULL,NULL,NULL);
        printf("fd_num : %d\n" , fd_num);

        //如果是套接字可讀
        if(FD_ISSET(sockfd,&rset)){
            //伺服器關閉
            if((n=read(sockfd,buf,BUFSIZ)) <= 0){
                if(eof == 1){
                    puts("server closed");
                    break;
                }
                else {
                    puts("server ter");
                    break;
                }

            }
            buf[n] = 0;
            printf("recv from serv:%s\n" , buf);
        }

        //如果是輸入端可讀
        if(FD_ISSET(0,&rset)){

            //如果ctrl + d
            if((n = read(0,buf,BUFSIZ)) <= 0){
                printf("client closing\n");

                //先發送Fin , 等服務端close
                shutdown(sockfd,SHUT_WR);
                eof = 1;
                FD_CLR(0,&rset);
                continue;
            }
            write(sockfd,buf,n);
        }
    }
}