下面有9种服务器模型分别是:

  • 迭代服务器。
  • 并发服务器,为每个客户fork一个进程。
  • 预先派生子进程,每个子进程都调用accept,accept无上锁保护。
  • 预先派生子进程,以文件锁的方式保护accept。
  • 预先派生子进程,以线程互斥锁上锁的方式保护accept。
  • 预先派生子进程,由父进程向子进程传递套接口描述字。
  • 并发服务器,为每个客户请求创建一个线程。
  • 预先创建线程,以互斥锁上锁方式保护accept。
  • 预先创建线程,由主线程调用accept,并把每个客户连接传递给线程池中某个可用线程。

1、迭代服务器

       典型代码:
socket
bind
listen
for(;;)
{
connfd = accept(listenfd, (SA*)&cliaddr, &clilen);
process_connection(connfd);
close(connfd);
}
       优点:主要是编程简单。
       缺点:处理完一个连接之后才能处理下一个连接,无并发可言,应用很少。
2.并发服务器,为每个客户fork一个进程
       典型代码:
init_address(server_addr)
listenfd = socket(AF_INET,SOCKET_STREAM,0);
bind(listenfd, (SA*)server_addr, sizeof(serveraddr));
listen(listenfd,BACKLOG);
for(;;)
{
connfd = accept(listenfd, (SA*)&cliaddr, &clilen);
if(connfd < 0)
{
if(EINTR == errno)
continue;
else
//error
} if(fork() == 0)
{
close(c=listenfd);
process_connection(connfd); //child process
exit(0);
}
close(connfd);//parent, close connected socket
}
       早期的网络服务器每天处理几百或者几千个客户连接时,这种服务器模型还是可以应付的。
       但是随着互联网业务的迅猛发展,繁忙的web服务器每天可能需要处理千万以上的连接,发服务器的问题在于为每个客户现场fork一个子进程比较消耗cpu时间。
3.预先派生子进程,每个子进程都调用accept,accept无上锁保护
     缺点:
  • 服务器必须在启动的时候判断需要预先派生多少子进程
  • 惊群现象(一个连接到来唤醒所有监听进程),不过较新版本的linux貌似修正了这个问题
     优点:无须引入父进程执行fork的开销就能处理新到的客户

4.预先派生子进程,以文件锁的方式保护accept
       本模型与3的区别仅仅是对accept(listenfd)使用了文件锁。
       这种模型是为了解决以库函数的形式实现的accept不能在多个进程中引用同一个监听套接口的问题(源自BSD的unix,在内核中实现的accept可以引用)。使用文件锁保证了每个连接到来,只有一个进程阻塞在accept调用上。对于已经在内核中实现了accept的系统来说,这种模型至少增加了加锁解锁的开销,所以相对于第3种模型性能较低(特别是在消除了惊群问题的系统上)
5.预先派生子进程,以线程互斥锁上锁的方式保护accept
       典型代码:
static pthread_mutex_t *mptr;
void
my_lock_init(char *pathname)
{
int fd;
pthread_mutexattr_t mattr;
//因为是相关进程所以可以使用/dev/zero设备创建共享内存
//优势是调用mmap创建共享存储区之前无需一个实际的文件
//映射/dev/zero自动创建一个指定长度的映射区
fd = open("/dev/zero", O_RDWR, 0,);
// 将mptr映射到共享存储区
mptr = mmap(0, sizeof(pthread_mutex_t), PORT_READ | PORT_WRITE, MAP_SHARED, fd, 0);
close(fd);
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mptr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mptr, &mattr);
} void
my_lock_wait()
{
pthread_mutex_lock(mptr);
} void
my_lock_release()
{
pthread_mutex_unlock(mptr);
} int
main(int argc, char **argv)
{
//init socket and address
my_lock_init(pathname);
for(i = 0; i < nchildren; ++i)
{
pids[i] = child_make(i, listenfd, addrlen);
} for(;;)
pause();
} pid_t
child_make(int i, int listenfd, int addrlen)
{
pid_t pid; if((pid = fork) > 0)
return pid; child_main(i, listenfd, addrlen);
} void
child_main()
{
for(;;)
{
my_lock_wait();
connfd = accept(listenfd, chiladdr, &clilen);
my_lock_release();
web_child(connfd);
close(connfd);
}
}
       这种模型在模型4上做出了进一步的改进,由于以文件锁的方式实现保护会涉及文件系统,这样可能比较耗时,所以改进的办法是以pthread mutex互斥量代替文件锁。使用线程上锁保护accept不仅适用于同一进程内各线程上锁,也适用于不同进程间上锁在多进程环境下使用线程互斥锁实现同步有两点要求:
1.互斥锁必须放在由所有进程共享的内存区
2.必须告知线程库这是在不同的进程间共享的互斥锁
       注意:目前很火的高性能web服务器的代表nginx就是采用的这种模型,网络上对nginx的研究很多。
6.预先派生子进程,由父进程向子进程传递套接口描述字
     优势:不需要对accept上锁保护
     劣势:
  • 编码复杂—父进程必须跟踪子进程的忙闲状态,以便给空闲子进程传递新的套接口。在前述的预先派生子进程的例子中,父进程无需关心由哪一个子进程接收一个客户连接,操作系统会根据调度算法处理这些细节。采用这种模型的结果是这些进程无法均衡的处理连接。
  • 父进程通过字节流管道把描述子传递到各个子进程,并且各个子进程通过字节流管道写回单个字节,比起无论是使用共享内存区中的互斥锁还是使用文件锁实施的上锁和解锁都更费时。
7.并发服务器,为每个客户请求创建一个线程
       典型代码:
for(;;)
{
connfd = accept(listenfd, cliaddr, &clilen,);
pthread_create(&tid, NULL, &doit, (void *)connfd);
} void *
doit(void *arg)
{
pthread_detach(pthread_self());
web_child((int)arg);
close((int)arg)
return (NULL);
}
       优点:编码简单。
       缺点:现场为每个连接创建线程相对于预先派生线程池来说比较耗时。
8.预先创建线程,以互斥锁上锁方式保护accept
    优势:
  • 编程简介,易于理解
  • 线程池的方式避免了现场创建线程的开销
  • OS线程调度算法保证了线程负载的均衡性
       这就是leader-follower模式一个线程等待连接到来,其他线程休眠;新连接到来后leader去处理连接,释放listenfd,其他线程竞抢监听套接口listenfd(可能有惊群的问题)。leader在处理完连接以后成为follower。
9.预先创建线程,由主线程调用accept,并把每个客户连接传递给线程池中某个可用线程
       劣势:相对于模型8,该模型不仅需要使用pthread mutex额外还需要使用pthread cond。

UNIX网络编程——常用服务器模型总结的更多相关文章

  1. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

  2. UNIX网络编程——并发服务器(TCP)

    在迭代服务器中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现. 网络服务器通常用fork来同时 ...

  3. UNIX网络编程——getsockname和getpeername函数

    UNIX网络编程--getsockname和getpeername函数   来源:网络转载   http://www.educity.cn/linux/1241293.html     这两个函数或者 ...

  4. 【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

    RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [ ...

  5. 【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)

    RT,使用消息队列,信号量和命名管道实现的多人群聊系统. 本学期Linux.unix网络编程的第三个作业. 先上实验要求: 实验三  多进程服务器 [实验目的] 1.熟练掌握进程的创建与终止方法: 2 ...

  6. 【Linux/unix网络编程】之使用socket进行TCP编程

    实验一 TCP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本TCP通讯,实现服务器与客户端的信息交互. [实验学时] 4学时 [实验内容] 实现一个服务器 ...

  7. Unix网络编程--卷二:进程间通信

    Unix网络编程--卷二:进程间通信 本书是一部Unix网络编程的经典之作!进程间通信(IPC)几乎是所有Unix程序性能的关键,理解IPC也是理解如何开发不同主机网络应用程序的必要条件.本书从对Po ...

  8. Unix网络编程--卷一:套接字联网API

    UNIX网络编程--卷一:套接字联网API 本书面对的读者是那些希望自己编写的程序能够使用成为套接字(socket)的API进行彼此通信的人. 目录: 0.准备环境 1.简介 2.传输层:TCP.UD ...

  9. [转载] 读《UNIX网络编程 卷1:套接字联网API》

    原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网 ...

随机推荐

  1. hdu5586 BestCoder Round #64 (div.2)

    问题描述 给n个数{A}_{1},{A}_{2}....{A}_{n}A​1​​,A​2​​....A​n​​,你可以选择一个区间(也可以不选),区间里每个数x变成f(x),其中f(x)=(1890x ...

  2. IDEA 整合 SSM 框架学习

    认识 Spring 框架 更多详情请点击这里:这里 Spring 框架是 Java 应用最广的框架,它的成功来源于理念,而不是技术本身,它的理念包括 IoC (Inversion of Control ...

  3. c++中sizeof的用法

    /*测试sizeof() 测试环境:windows 7 64位操作系统 VS2012编译器 */ #include <iostream> using namespace std; int ...

  4. 转载:使用Math.floor和Math.random取随机整数

    Math.random():获取0~1随机数 Math.floor() method rounds a number DOWNWARDS to the nearest integer, and ret ...

  5. Java并发之BlockingQueue的使用

    Java并发之BlockingQueue的使用 一.简介 前段时间看到有些朋友在网上发了一道面试题,题目的大意就是:有两个线程A,B,  A线程每200ms就生成一个[0,100]之间的随机数, B线 ...

  6. css文本超出省略号

    终于完成了项目了,今天我就分享自己在项目中比较实用的一些功能的实现,第一个就是纯css文本超出省略号 /* 文本单行超出省略号 */ .textels { overflow: hidden; text ...

  7. spark on yarn 运行问题记录

    问题一: 18/03/15 07:59:23 INFO yarn.Client: client token: N/A diagnostics: Application application_1521 ...

  8. 游戏流程&游戏规则

  9. c++ 深入理解数组

    阅读前提:你得知道啥是数组. 本文需要弄清楚的问题如下: 1,数组作为函数参数,传入的是值,还是地址? 2,数组作为函数参数,数组的长度能否确定? 解决如下 1,数组作为函数参数,传入的是地址.因为数 ...

  10. 关于go语言的通道

    1.记一次gorountine导致的泄漏 在项目中使用https://github.com/deckarep/golang-set这个三方包造成了gorountine泄漏.先来看一下这个包的迭代器设置 ...