进程模型服务器端

进程模型服务器端修炼主要包括以下境界

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;
}
时间: 2024-11-06 07:41:41

进程模型服务器端的相关文章

nginx源码分析--高性能服务器开发 常见进程模型

1.高性能服务器 对一个高性能服务器来说,处理速度快和资源占用小是典型特性,尤其是当服务器遇到C10K问题的时候(网络服务器在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被称为C10K问题).要做到处理速度足够快,其并发模型的设计相当关键,而要做到资源尤其是内存资源的占用少,就要依赖于其资源分配和资源管理的方案设计. 服务器的并发模型设计是网络编程中很关键的一个部分,服务器的并发量取决于两个因素,一个是提供服务的进程数量,另外一个是每个进程可同时处理的并发连接数量.相应的,服务器

nginx进程模型 master/worker

nginx有两类进程,一类称为master进程(相当于管理进程),另一类称为worker进程(实际工作进程).启动方式有两种: (1)单进程启动:此时系统中仅有一个进程,该进程既充当master进程的角色,也充当worker进程的角色. (2)多进程启动:此时系统有且仅有一个master进程,至少有一个worker进程工作. master进程主要进行一些全局性的初始化工作和管理worker的工作:事件处理是在worker中进行的. 首先简要的浏览一下nginx的启动过程,如下图: 2.实现原理

processModel与ASP.NET进程模型

配置 Microsoft Internet 信息服务 (IIS) Web 服务器上的 ASP.NET 进程模型设置.其作用是配置IIS或IIS中的应用程序池(IIS7及以后版本)的安全性,性能,健壮性,可靠性. processModel 节只能在 Machine.config 文件中进行设置,它影响服务器上运行的所有 ASP.NET 应用程序.Machine.config文件则位于Windows\Microsoft.NET\Framework64\{.Net Framework Version}

Esper学习之三:进程模型

之前对Esper所能处理的事件结构进行了概述,并结合了例子进行讲解,不清楚的同学请看Esper学习之二:事件类型.今天主要为大家解释一下Esper是怎么处理事件的,即Esper的进程模型. 1.UpdateListener UpdaterListener是Esper提供的一个接口,用于监听某个EPL在引擎中的运行情况,即事件进入并产生结果后会通知UpdateListener.接口如下 [java] view plaincopy package com.espertech.esper.client

nginx源码分析--master和worker进程模型

一.Nginx整体架构 正常执行中的nginx会有多个进程,最基本的有master process(监控进程,也叫做主进程)和woker process(工作进程),还可能有cache相关进程. 一个较为完整的整体框架结构如图所示: 二.核心进程模型 启动nginx的主进程将充当监控进程,而由主进程fork()出来的子进程则充当工作进程. nginx也可以单进程模型执行,在这种进程模型下,主进程就是工作进程,没有监控进程. Nginx的核心进程模型框图如下: master进程 监控进程充当整个进

nginx源码分析--框架设计 &amp; master-worker进程模型

Nginx的框架设计-进程模型 在这之前,我们首先澄清几点事实: nginx作为一个高性能服务器的特点,其实这也是所有的高性能服务器的特点,依赖epoll系统调用的高效(高效是相对select/poll这些系统调用的,底层有一个链表和红黑树,避免了轮询,减少了用户空间和系统空间之间的数据传递等),非阻塞(所有的操作都是非阻塞,这样),多进程(master-slave进程模型),这些事实使得nginx成为一个高性能服务器的前提条件. 既然作为一个软件(http服务器),相对于一个网络库而言肯定有更

Nginx(十)-- 进程模型及工作原理

1.nginx进程模型 Nginx是一个master和worker的模型.master主要用来管理worker进程,master就比作老板,worker就是打工仔,master指挥worker来做事情.下图是nginx的进程模型: master进程: 1.接收外界的信号,例如:kill -QUIT,kill -HUP   kill -HUP 重新加载配置文件,然后重新启动新的worker进程,老的还在运行,同时,向老的worker进程发送退休命令,老的worker进程将原有的请求处理完之后,就退

【nginx】【转】Nginx核心进程模型

一.Nginx整体架构 正常执行中的nginx会有多个进程,最基本的有master process(监控进程,也叫做主进程)和woker process(工作进程),还可能有cache相关进程. 一个较为完整的整体框架结构如图所示: 二.核心进程模型 启动nginx的主进程将充当监控进程,而由主进程fork()出来的子进程则充当工作进程. nginx也可以单进程模型执行,在这种进程模型下,主进程就是工作进程,没有监控进程. Nginx的核心进程模型框图如下: master进程 监控进程充当整个进

Nginx学习——进程模型(worker进程)

进程模型 worker进程 master进程模型核心函数ngx_master_process_cycle()中调用了创建子进程函数ngx_start_worker_processes(),该函数源码如下 static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { ngx_int_t i; ngx_channel_t ch; ngx_log_error(NGX_LOG_NOTIC