程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 進程模型服務器端,模型服務器端

進程模型服務器端,模型服務器端

編輯:關於C語言

進程模型服務器端,模型服務器端


進程模型服務器端修煉主要包括以下境界

1.每個進程對應一個連接

2.預先創建一定量的進程,當連接到來時,拿一個進程來對付他,個人稱它為靜態進程池

3.預先創建一定量的進程,當連接到來時,拿一個進程來對付他,如果沒有進程,嘗試創建新進程,當進程過多時,關閉一些進程,此乃動態調整的進程池模型。

4.與其他模型聯合使用,比如說和線程模型,和IO復用模型合用

此文提出第一第二境界的解決方案。

 

本文包括第一和第二點,後面兩點涉及知識點稍多。暫時沒能很好的應用。

進程——連接:對於每個連接,fork一個進程來處理連接,處理結束即退出子進程

優點:簡單,非常簡單

缺點:效率不行,並發訪問不行,大量連接不行。

對於此類模型,代碼相對容易,服務器端如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>

#define MAX_BUF    1024


int setup(char *ip, int port){
    /* variable */
    int sock_fd, connect_fd;
    struct sockaddr_in server, client;
    int ret;
    /* socket */
    sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(sock_fd < 0){
        perror("socket failed");
        exit(1);
    }

    server.sin_family = PF_INET;
    //server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);
    if(inet_pton(PF_INET, ip, &server.sin_addr) < 0){
        perror("inet_pton");
        exit(1);
    }
    /* bind */
    ret = bind(sock_fd, (struct sockaddr*)&server, sizeof(server));
    if(ret < 0){
        perror("bind failed");
        exit(1);
    }
    /* listen */
    if(listen(sock_fd, 5)<0){
        perror("listen failed\n");
        exit(1);
    }
    return sock_fd;
}

//處理模型
void process_mode(int sock_fd, int connect_fd){
    char buff[MAX_BUF];
    int ret = -1;
    pid_t pid;
    pid = fork();
    if(pid<0){
        perror("fork error");
        exit(errno);
    }
    //子進程
    else if(pid == 0){

        close(sock_fd);
        if((ret = recv(connect_fd, buff, sizeof(buff), 0)) < 0){
            perror("recv");
            exit(1);
        }        
        else if(ret == 0)
            printf("read end\n");
        else{
            fprintf(stderr,"receive message %s retval:%d\n",
             buff, ret);
        }
        close(connect_fd);
        exit(0);
    }
    total_count++;    
    close(connect_fd);
}


int main(int argc, char **argv){

    int connect_fd;
    if(argc != 3){
        fprintf(stderr,"usage <ip><port>");
        exit(-1);
    }
    
    //setup
    int sock_fd = setup(argv[1], atoi(argv[2]) );
    printf("network setup successfully.\nip:%s port:%s\n",
         argv[1], argv[2]);
    /* accept */
    while(1){
        connect_fd = accept(sock_fd, (struct sockaddr*)NULL,NULL);    
        process_mode(sock_fd, connect_fd);    
    }    
    
    return 0;    
}

 

值得注意的一點是:fork之後要關閉原本綁定的套接字,父進程要關閉連接套接字,這是fork帶來的影響,關閉描述符並不會帶來資源銷毀,只要描述符引用不為0即可

為了克服上面模型中效率低下的缺點,可以預先fork一定量的進程,當連接到來時就不用重新fork了,處理完客戶請求之後,不是退出,而是繼續等待請求。由此產生靜態進程池。

靜態線程池的實現相對簡單。難點是設計的時候要防止驚群現象。所謂驚群就類似一群鴿子在吃東西你跑去了全部鴿子都跑了。為杜絕驚群現象,需要加鎖。

此類模型優點是避免fork帶來的效率上的降低。

缺點是效率還是不夠高,當進程池中進程不足時,不能動態調整池中進程個數。當連接很少時,池中進程數過多,這也是一種浪費。

具體實現如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#define MAX_BUF    1024


/* 靜態池 */
typedef struct static_pool{
    int nchild;
    pid_t *pids;
}spool;

/* 服務器結構 */
typedef struct Server{

}Server, *pServer;

spool pool;

/* 啟動 */
int setup(char *ip, int port){
    /* variable */
    int sock_fd, connect_fd;
    struct sockaddr_in server, client;
    int ret;
    /* socket */
    sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(sock_fd < 0){
        perror("socket failed");
        exit(1);
    }

    server.sin_family = PF_INET;
    //server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);
    if(inet_pton(PF_INET, ip, &server.sin_addr) < 0){
        perror("inet_pton");
        exit(1);
    }
    /* bind */
    ret = bind(sock_fd, (struct sockaddr*)&server, sizeof(server));
    if(ret < 0){
        perror("bind failed");
        exit(1);
    }
    /* listen */
    if(listen(sock_fd, 5)<0){
        perror("listen failed\n");
        exit(1);
    }
    return sock_fd;
}

//SIGNAL INT int3中斷
void sig_call_back(int signo){
    int i;

    for(i = 0; i<pool.nchild; i++)
        kill(pool.pids[i], SIGTERM);
    while(wait(NULL)>0);

    if(errno != ECHILD){
        perror("wait");
        exit(errno);
    }
    exit(0);
}

//封裝鎖操作, reference:Unix Network Programming

struct flock lock_it, unlock_it;
int lock_fd = -1;
/* 初始化信息 */
void my_lock_init(char *pathname){
    
    char lock_file[1024];

    strncpy(lock_file, pathname, sizeof(lock_file));
    lock_fd = mkstemp(lock_file);
    if(lock_fd < 0){
        perror("mkstemp");
        exit(errno);
    }
    unlink(lock_file);
    lock_it.l_type = F_WRLCK;
    lock_it.l_whence = SEEK_SET;
    lock_it.l_start = 0;
    lock_it.l_len = 0;
    
    unlock_it.l_type = F_UNLCK;
    unlock_it.l_whence = SEEK_SET;
    unlock_it.l_start = 0;
    unlock_it.l_len = 0;
    
}
/* 鎖等待 */
void my_lock_wait(){
    int rc;
    
    while((rc = fcntl(lock_fd, F_SETLKW, &lock_it))<0){
        if(errno == EINTR)
            continue;
        else{
            perror("fcntl");
            exit(errno);
        }
    }
}

/* 釋放鎖 */
void my_lock_release(){
    if(fcntl(lock_fd, F_SETLKW, &unlock_it) < 0){
        perror("fcntl");
        exit(errno);
    }
}

/* 處理請求, 此處為空 */
void process(int connect_fd){

}

/* 等待請求 */
void child_loop(int sock_fd){
    
    int connect_fd;
    socklen_t client_len;
    struct sockaddr_in client;
    memset(&client, 0, sizeof(client));
    while(1){
        client_len = sizeof(struct sockaddr_in);
        my_lock_wait();
        connect_fd = accept(sock_fd, (struct sockaddr*)&client, &client_len);
        printf("process %d deal with connnector\n", getpid());
        process(connect_fd);
        close(connect_fd);
        my_lock_release();
    }
}

/* 產生子進程,子進程接受請求並處理請求 */
int make_child(int sock_fd){
    pid_t pid;
    if((pid = fork()) > 0)
        return pid;
    child_loop(sock_fd);
}

/* 預先fork */
void preprocess(int sock_fd, int n){
    
    int i = 0;
    pool.nchild = n;
    pool.pids = (pid_t*)malloc(sizeof(pid_t) * n);    
    if(pool.pids == NULL){
        perror("malloc");
        exit(-1);
    }
    
    //生娃
    my_lock_init("/tmp/lock.XXXXXX");
    for(i = 0; i<n; i++)
        pool.pids[i] = make_child(sock_fd);
}


int main(int argc, char **argv){

    if(argc != 4){
        fprintf(stderr,"usage <ip><port><process num>");
        exit(-1);
    }
    //setup
    int sock_fd = setup(argv[1], atoi(argv[2]) );
    printf("network setup successfully.\nip:%s port:%s\n",
         argv[1], argv[2]);
    
    preprocess(sock_fd, atoi(argv[3]));    
    signal(SIGINT, sig_call_back);
    for(;;)
        pause();
    return 0;    
}

 

代碼不難,注意點卻是不少。一個是加鎖,一個是父進程結束時候要把所有子進程都殺掉,避免產生孤兒進程。

用於測試的客戶端例子,對於上面兩個模型都適用。

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define LEN(str) (sizeof(char)*strlen(str))

void connect_server(char *ip, int port){
    
    /* variable */
    int sock_fd;
    struct sockaddr_in server;
    char buff[1024];
    int ret;

    /* socket */
    sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(sock_fd < 0)    {
        perror("socket failed");
        exit(1);
    }

    server.sin_family = PF_INET;
    server.sin_port = htons(port);
    if(inet_pton(PF_INET, ip, &server.sin_addr) < 0){
        perror("inet_pton");
        exit(1);
    }
    /* connect */
    if((ret = connect(sock_fd, (struct sockaddr*)&server, 
                sizeof(server)) )< 0){
        perror("connect failed");
        exit(1);
    }
    /* send buff */
    sprintf(buff, "Hello World");
    if(( ret = send(sock_fd, buff, LEN(buff), 0)) < 0){
        perror("send");
        exit(1);
    }
    printf("send msg\n");

    /* close */
    close(sock_fd);
}

int main(int argc, char **argv){
    int i;
    if(argc < 4){
        perror("usage<ip><port><connect count>");
        exit(-1);
    }
    for(i = 0; i< atoi(argv[3]); i++)
        connect_server(argv[1], atoi(argv[2]) );
    return 0;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved