歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux C語言 網絡編程(二) 服務器模型

前面介紹了關於連接linux服務端方式,但是服務端的資源是有限的,所以我們通常需要重新思考,設計一套服務器模型來處理對應的客戶端的請求。

第一種:並發服務器,通過主進程統一處理客戶端的連接,當客戶端連接過後,臨時fork()進程,由子進程處理客戶端請求,將連接請求和業務進行了分離。

server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
static void handle_request(int s_c)
{
    time_t now;                                 /*時間*/
    char buff[BUFFLEN];                         /*收發數據緩沖區*/
    int n = 0;
    memset(buff, 0, BUFFLEN);                   /*清零*/
    n = recv(s_c, buff, BUFFLEN,0);         /*接收發送方數據*/
    if(n > 0 && !strncmp(buff, "TIME", 4))      /*判斷是否合法接收數據*/
    {
        memset(buff, 0, BUFFLEN);               /*清零*/
        now = time(NULL);                       /*當前時間*/
        sprintf(buff, "%24s\r\n",ctime(&now));  /*將時間復制入緩沖區*/
        send(s_c, buff, strlen(buff),0);        /*發送數據*/
    }       
    /*關閉客戶端*/
    close(s_c); 
}
static int handle_connect(int s_s)
{

    int s_c;                                /*客戶端套接字文件描述符*/
    struct sockaddr_in from;                /*客戶端地址*/
    socklen_t len = sizeof(from);

    /*主處理過程*/
    while(1)
    {
        /*接收客戶端連接*/
        s_c = accept(s_s, (struct sockaddr*)&from, &len);
        if(s_c > 0)                         /*客戶端成功連接*/
        {
            /*創建進程進行數據處理*/
            if(fork() > 0){                 /*父進程*/
                close(s_c);                 /*關閉父進程的客戶端連接套接字*/
            }else{
                handle_request(s_c);        /*處理連接請求*/
                return(0);  
            }
        }
    }       
}
int main(int argc, char *argv[])
{
    int s_s;                                /*服務器套接字文件描述符*/
    struct sockaddr_in local;               /*本地地址*/    

    /*建立TCP套接字*/
    s_s = socket(AF_INET, SOCK_STREAM, 0);

    /*初始化地址*/
    memset(&local, 0, sizeof(local));       /*清零*/
    local.sin_family = AF_INET;             /*AF_INET協議族*/
    local.sin_addr.s_addr = htonl(INADDR_ANY);  /*任意本地地址*/
    local.sin_port = htons(SERVER_PORT);    /*服務器端口*/

    /*將套接字文件描述符綁定到本地地址和端口*/
    bind(s_s, (struct sockaddr*)&local, sizeof(local));
    listen(s_s, BACKLOG);               /*偵聽*/

    /*處理客戶端連接*/
    handle_connect(s_s);

    close(s_s);

    return 0;
}

代碼比較詳細,容易理解。

下面介紹客戶端代碼,後面的客戶端代碼都是一樣的。
client.c

#include 
#include 
#include 
#include 
#include 
#include 
#define BUFFLEN 1024
#define SERVER_PORT 8888
int main(int argc, char *argv[])
{
    int s;                                      /*服務器套接字文件描述符*/
    struct sockaddr_in server;                  /*本地地址*/
    char buff[BUFFLEN];                         /*收發數據緩沖區*/
    int n = 0;                                  /*接收字符串長度*/

    /*建立TCP套接字*/
    s = socket(AF_INET, SOCK_STREAM, 0);

    /*初始化地址*/
    memset(&server, 0, sizeof(server));     /*清零*/
    server.sin_family = AF_INET;                /*AF_INET協議族*/
    server.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/
    server.sin_port = htons(SERVER_PORT);       /*服務器端口*/   

    /*連接服務器*/
    connect(s, (struct sockaddr*)&server,sizeof(server));
    memset(buff, 0, BUFFLEN);                   /*清零*/
    strcpy(buff, "TIME");                       /*復制發送字符串*/
    /*發送數據*/
    send(s, buff, strlen(buff), 0);
    memset(buff, 0, BUFFLEN);                   /*清零*/
    /*接收數據*/    
    n = recv(s, buff, BUFFLEN, 0);
    /*打印消息*/
    if(n >0){
        printf("TIME:%s",buff); 
    }
    close(s);

    return 0;
}

第二種模型:通過線程來處理,線程比進程占用資源少,效率高,數據共享。通過pthread_create()建立一個連接請求處理,線程處理函數為handle_request().
server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
static void handle_request(void *argv)
{
    int s_c = *((int*)argv);
    time_t now;                                 /*時間*/
    char buff[BUFFLEN];                         /*收發數據緩沖區*/
    int n = 0;
    memset(buff, 0, BUFFLEN);                   /*清零*/
    n = recv(s_c, buff, BUFFLEN,0);         /*接收發送方數據*/
    if(n > 0 && !strncmp(buff, "TIME", 4))      /*判斷是否合法接收數據*/
    {
        memset(buff, 0, BUFFLEN);               /*清零*/
        now = time(NULL);                       /*當前時間*/
        sprintf(buff, "%24s\r\n",ctime(&now));  /*將時間復制入緩沖區*/
        send(s_c, buff, strlen(buff),0);        /*發送數據*/
    }       
    /*關閉客戶端*/
    close(s_c); 
}
static void handle_connect(int s_s)
{

    int s_c;                                    /*客戶端套接字文件描述符*/
    struct sockaddr_in from;                    /*客戶端地址*/
    socklen_t len = sizeof(from);
    pthread_t  thread_do;

    /*主處理過程*/
    while(1)
    {
        /*接收客戶端連接*/
        s_c = accept(s_s, (struct sockaddr*)&from, &len);
        if(s_c > 0)                         /*客戶端成功連接*/
        {
            /*創建線程處理連接*/
            pthread_create(&thread_do,
                    NULL,
                    (void*)handle_request,
                    &s_c);              
        }
    }       
}
int main(int argc, char *argv[])
{
    int s_s;                                /*服務器套接字文件描述符*/
    struct sockaddr_in local;               /*本地地址*/    

    /*建立TCP套接字*/
    s_s = socket(AF_INET, SOCK_STREAM, 0);

    /*初始化地址和端口*/
    memset(&local, 0, sizeof(local));       /*清零*/
    local.sin_family = AF_INET;             /*AF_INET協議族*/
    local.sin_addr.s_addr = htonl(INADDR_ANY);  /*任意本地地址*/
    local.sin_port = htons(SERVER_PORT);        /*服務器端口*/

    /*將套接字文件描述符綁定到本地地址和端口*/
    bind(s_s, (struct sockaddr*)&local, sizeof(local));
    listen(s_s, BACKLOG);                   /*偵聽*/

    /*處理客戶端連接*/
    handle_connect(s_s);

    close(s_s);

    return 0;       
}

第三種:服務端各線程獨自accept(),使用互斥鎖,使用pthread_create()建立多個線程組成的線程池,主線程等待程序結束,各個線程獨自接收客戶端accept,以及後面數據處理。
server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define CLIENTNUM 2
/*互斥量*/
pthread_mutex_t ALOCK = PTHREAD_MUTEX_INITIALIZER;
static void *handle_request(void *argv)
{   
    int s_s = *((int*)argv);
    int s_c;                                /*客戶端套接字文件描述符*/
    struct sockaddr_in from;                /*客戶端地址*/
    socklen_t len = sizeof(from);
    for(;;)
    {
        time_t now;                         /*時間*/
        char buff[BUFFLEN];                 /*收發數據緩沖區*/
        int n = 0;

        pthread_mutex_lock(&ALOCK);         /*進入互斥區*/
        s_c = accept(s_s, (struct sockaddr*)&from, &len);
        /*接收客戶端的請求*/
        pthread_mutex_unlock(&ALOCK);       /*離開互斥區*/

        memset(buff, 0, BUFFLEN);           /*清零*/
        n = recv(s_c, buff, BUFFLEN,0); /*接收發送方數據*/
        if(n > 0 && !strncmp(buff, "TIME", 4))  /*判斷是否合法接收數據*/
        {
            memset(buff, 0, BUFFLEN);       /*清零*/
            now = time(NULL);               /*當前時間*/
            sprintf(buff, "%24s\r\n",ctime(&now));  /*將時間復制入緩沖區*/
            send(s_c, buff, strlen(buff),0);        /*發送數據*/
        }       
        /*關閉客戶端*/
        close(s_c); 
    }

    return NULL;
}
static void handle_connect(int s)
{   
    int s_s = s;
    pthread_t  thread_do[CLIENTNUM];        /*線程ID*/
    int i = 0;
    for(i=0;i

第四種:IO復用服務器,並發服務器客戶端越多,對服務器造成的壓力越大,所以還有第四種模型,IO復用函數用select來做。
server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define CLIENTNUM 1024                      /*最大支持客戶端數量*/

/*可連接客戶端的文件描述符數組*/
int connect_host[CLIENTNUM];
int connect_number =  0;
static void *handle_request(void *argv)
{   
    time_t now;                                 /*時間*/
    char buff[BUFFLEN];                         /*收發數據緩沖區*/
    int n = 0;

    int maxfd = -1;                             /*最大偵聽文件描述符*/
    fd_set scanfd;                              /*偵聽描述符集合*/
    struct   timeval   timeout;                     /*超時*/
    timeout.tv_sec     =   1;                   /* 阻塞1s後超時返回 */     
    timeout.tv_usec   =   0;     

    int i = 0;
    int err  = -1;
    for(;;)
    {   
        /*最大文件描述符值初始化為-1*/      
        maxfd = -1;
        FD_ZERO(&scanfd);                       /*清零文件描述符集合*/
        for(i=0;i 0 && !strncmp(buff, "TIME", 4))
                        /*判斷是否合法接收數據*/
                        {
                            memset(buff, 0, BUFFLEN);           /*清零*/
                            now = time(NULL);       /*當前時間*/
                            sprintf(buff, "%24s\r\n",ctime(&now));
                            /*將時間復制入緩沖區*/
                            send(connect_host[i], buff, strlen(buff),0);
                            /*發送數據*/
                        }
                        /*更新文件描述符在數組中的值*/
                        connect_host[i] = -1;
                        connect_number --;  /*客戶端計數器減1*/    
                        /*關閉客戶端*/
                        close(connect_host[i]);
                    } 
                }
                break;  
        }         
    } 

    return NULL;
}
static void *handle_connect(void *argv)
{   
    int s_s = *((int*)argv) ;           /*獲得服務器偵聽套接字文件描述符*/
    struct sockaddr_in from;
    socklen_t len = sizeof(from);
    /*接收客戶端連接*/
    for(;;)
    {
        int i = 0;
        int s_c = accept(s_s, (struct sockaddr*)&from, &len);
        /*接收客戶端的請求*/
        printf("a client connect, from:%s\n",inet_ntoa(from.sin_addr));
        /*查找合適位置,將客戶端的文件描述符放入*/             
        for(i=0;i

選擇合適的服務器模型十分重要,對於編程有很大的影響。

Copyright © Linux教程網 All Rights Reserved