歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux 服務器模型小結

Linux 服務器模型小結

日期:2017/3/1 9:09:39   编辑:Linux編程

當我們用socket進行編程的時候,細節上都是選擇一個AF_LOCAL,AF_INET再根據相應的類型填充地址,其實根據通信需求,有幾種簡單的服務模型可供選用,掌握了這些框架再結合socket高度的抽象,可以為我們編寫簡單的服務器程序提供指導

循環服務

用戶請求服務需要排隊,服務器一次只能服務一個客戶,服務完才能對下一個客戶進行服務。ATM機就是這個1vs1模型。udp服務器也經常使用這個模型

//模型偽代碼
main{
    //獲得偵聽文件描述符
    listenfd=socket();
    
    //准備地址addr
    
    //(listenfd,100);
    
    while(1){將服務器的addr和listenfd綁定
    bind(listenfd,addr)
    
    //開始偵聽,設置緩沖隊列長度
    listen(listenfd,100);
    
    while(1){
        //如果偵聽到任務就獲取sockfd
        int sockfd = accept(listenfd);  //listenfd默認是阻塞IO,沒任務時進程會sleep
        
        //通信...
        
        close(sockfd);
    }
}

多進程並發服務

多進程只是放listen到請求的時候,不是在原進程中處理請求,而是在子進程中處理,父進程繼續偵聽請求。多進程的好處父進程不必等待子進程處理完一個請求才能獲取下一個請求,而是只要有請求就fork一個子進程處理,這樣就可以實現並發服務器。多進程並發的更實際的方案是使用進程池來實現。

//模型偽代碼
main{
    //獲得偵聽文件描述符
    listenfd=socket();
    
    //准備地址addr
    
    //將服務器的addr和listenfd綁定
    bind(listenfd,addr)
    
    //開始偵聽,設/准備地址add置緩沖隊列長度
    listen(listenfd,100);
    
    while(1){
        //如果偵聽到任務就獲取sockfd
        int sockfd = accept(listenfd);  //listenfd默認是阻塞IO,沒任務時進程會sleep
        pid=fork();
        if(0 == pid){
            //子進程一定要首先關閉listenfd,防止和父進程一起偵聽
            //也可以在父進程socket(,,SOCK_CLOEXEC);
            close(listenfd);
                    
            while(1){
                ret=read(sockfd);
                if(0 == ret) break;
                //通信...
            }
        
            exit(0);
        }
    }
}

多線程並發服務

使用多線程實現並發和多進程類似,但是創建一個線程的開銷比創建一個進程小得多,需要注意的是使用多線程需要做好對臨界資源的保護。實際操作經常使用線程池來實現多線程並發服務。

//模型偽代碼
void* communicate(void* arg){
    int sockfd=(int)arg;
    while(1){
        ret=read(sockfd);
        if(0 == ret) break;
        //通信...
    }
}

main{
    //獲得偵聽文件描述符
    listenfd=socket();
    
    //准備地址addr
    
    //將服務器的addr和listenfd綁定
    bind(listenfd,addr)
    
    //開始偵聽,設置緩沖隊列長度
    listen(listenfd,100);
    
    while(1){
        //如果偵聽到任務就獲取sockfd
        //listenfd默認是阻塞IO,沒任務時進程會sleep
        int sockfd = accept(listenfd);  
        pthread_create(tid,communicate,(void*)&sockfd);
        }
    }
}

I/O多路復用並發服務

I/O多路復用實現並發服務我已經在Linux I/O多路復用一文中舉出了詳細的例子,其實質就是將udp服務器的循環模型用在tcp上。

//模型偽代碼
main{
    //獲得偵聽文件描述符
    listenfd=socket();
    
    //准備地址addr
    
    //將服務器的addr和listenfd綁定
    bind(listenfd,addr)
    
    //開始偵聽,設置緩沖隊列長度
    listen(listenfd,100);
    
    //准備偵聽對象,和相應的觸發事件的集合
    monitor_set_1[i]={fds...}   //監控I/O有數據流入
    monitor_set_2[i]={fds...}   //監控I/O變得可寫

    while(1){
        //監控對象,如果有事件發生就返回
        poll(monitor_set_1,monitor_set_2)
        for(n=0;n<maxfd;n++){       //poll返回,說明有(一些)事件被觸發,依次處理這些觸發了事件的文件描述符
            if(一個fd有數據流入){
                if(是listenfd有數據流入){
                    //獲取sockfd
                    int sockfd = accept(listenfd)
                    
                    //將這個sockfd加入監聽有數據流入可寫的集合
                    add_mem(moniter_set_1,sockfd)
                }else {     //不是listenfd有數據流入,而是之前加入的sockfd有數據流入
                    
                    //讀取信息
                    read(fd,&msg)
                    
                    //將其挪入加入監控可寫事件的集合
                    add_mem(monitor_set_1,fd)
                }
            }else{  //一個fd變得可寫了
                
                //寫入信息
                write(fd,&msg)
                
                //將其挪入監控可讀的集合
                add_mem(monitor_set_1,fd)
            }
        }
    }
}
}

Copyright © Linux教程網 All Rights Reserved