下面有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. poj 2104 主席树(区间第k大)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 44940   Accepted: 14946 Ca ...

  2. [BZOJ]1031 字符加密Cipher(JSOI2007)

    持续划水中…… 感觉BZOJ上AC人数多的基本都是一些模板题,也就是某些算法的裸题.这些题目mark一下到时候回来复习也是不错的选择. Description 喜欢钻研问题的JS同学,最近又迷上了对加 ...

  3. bzoj2434阿狸的自动机

    转载自 http://www.cnblogs.com/zj75211/p/6934976.html ●BZOJ 2434: [Noi2011]阿狸的打字机   ●赘述题目 (题意就不赘述了) ●解法: ...

  4. PHP Laravel框架入门心得 | How to study PHP Laravel Framework

    PHP有不少开发框架,其中比较出名的有Symfony和Laravel. 我说说我最近入门Laravel的感受和学习方法吧. 1.第一个感受是Laravel的社区讨论和学习资源真的是太棒了,中文化也做得 ...

  5. Modern Algebra 读书笔记

    Modern Algebra 读书笔记 Introduction 本文是Introduction to Modern Algebra(David Joyce, Clark University)的读书 ...

  6. 模块机制 之commonJs、node模块 、AMD、CMD

    在其他高级语言中,都有模块中这个概念,比如java的类文件,PHP有include何require机制,JS一开始就没有模块这个概念,起初,js通过<script>标签引入代码的方式显得杂 ...

  7. org.apache.maven.archiver.MavenArchiver.getManifest

    eclipse导入新的maven项目时,pom.xml第一行报错: org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.mav ...

  8. python笔记九(迭代)

    一.迭代 通过for循环来遍历一个列表,我们称这种遍历的方式为迭代.只要是可迭代对象都可以进行迭代操作. 以下代码可以用来判断一个对象是否是可迭代的. 一类是集合数据类型,如list.tuple.di ...

  9. ACM 今年暑假不AC

    "今年暑假不AC?" "是的." "那你干什么呢?" "看世界杯呀,笨蛋!" "@#$%^&*%... ...

  10. UltraISO安装centos7系统

    1. 使用最新版UltraISO将ISO镜像刻录到U盘一定要是最新版,试用版都可以,按下图操作: 2. U盘启动电脑进入安装界面正常情况下你应该会看到下面的这个界面: 选择第一项,然后按TAB键(在评 ...