热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux并发服务器

Linux系统网络服务器模型主要有两种:并发服务器和循环服务器。所谓并发服务器就是在同一个时刻可以处理来自多个客户端的请求;循环服务器是指服务器在同一时

Linux系统网络服务器模型主要有两种:并发服务器和循环服务器。所谓并发服务器就是在同一个时刻可以处理来自多个客户端的请求;循环服务器是指服务器在同一时刻指可以响应一个客户端的请求。而且对于TCP和UDP套接字,这两种服务器的实现方式也有不同的特点。

1、TCP循环服务器:首先TCP服务器接受一个客户端的连接请求,处理连接请求,在完成这个客户端的所有请求后断开连接,然后再接受下一个客户端的请求。

   创建TCP循环服务器的算法如下:

    socket(……);   //创建一个TCP套接字

    bind(……);      //邦定公认的端口号

    listen(……);  //倾听客户端连接

    while(1)          //开始循环接收客户端连接

   {

                accept(……);//接收当前客户端的连接

                while(1)

                                   //处理当前客户端的请求

                         read(……);

                         process(……);

                          write(……);

                 }

                close(……);   //关闭当前客户端的连接,准备接收下一个客户端连接

    }

TCP循环服务器一次只处理一个客户端的请求,如果有一个客户端占用服务器不放时,其它的客户机连接请求都得不到及时的响应。因此,TCP服务器一般很少用循环服务器模型的。

 

2、TCP并发服务器:并发服务器的思想是每一个客户端的请求并不由服务器的主进程直接处理,而是服务器主进程创建一个子进程来处理。

    创建TCP并发服务器的算法如下:

    socket(……); //创建一个TCP套接字

    bind(……);    //邦定公认的端口号

    listen(……);//倾听客户端连接

    while(1)       //开始循环接收客户端的接收

   {

                accept(……);//接收一个客户端的连接

                if(fork(……)==0)  //创建子进程

                                

                         while(1)

                                        //子进程处理某个客户端的连接

                                   read(……);

                                   process(……);

                                   write(……);

                              }

                             close(……);  //关闭子进程处理的客户端连接

                              exit(……) ;//终止该子进程

                    }

                 close(……);           //父进程关闭连接套接字描述符,准备接收下一个客户端连接

       }

TCP并发服务器可以解决TCP循环服务器客户端独占服务器的情况。但同时也带来了一个不小的问题,即响应客户机的请求,服务器要创建子进程来处理,而创建子进程是一种非常消耗资源的操作。

 3、UDP循环服务器:UDP服务器每次从套接字上读取一个客户端的数据报请求,处理接收到的UDP数据报,然后将结果返回给客户机。

    创建UDP循环服务器的算法如下:

    socket(……);  //创建一个数据报类型的套接字

    bind(……);     //邦定公认的短口号

     while(1)        //开始接收客户端的连接

                     //接收和处理客户端的UDP数据报

                   recvfrom(……);

                   process(……);

                   sendto(……);

                  //准备接收下一个客户机的数据报

        }

因为UDP是非面向连接的,没有一个客户端可以独占服务器。只要处理过程不是死循环,服务器对于每一个客户机的请求总是能够处理的。

UDP循环服务器在数据报流量过大时由于处理任务繁重可能造成客户技数据报丢失,但是因为UDP协议本身不保证数据报可靠到达,所以UDP协议是尤许丢失数据报的。

 鉴于以上两点,一般的UDP服务器采用循环方式

4、UDP并发服务器

把并发的概念应用UDP就得到了并发UDP服务器,和并发TCP服务器模型一样是创建子进程来处理的。

 创建UDP并发服务器的算法如下:

     socket(……);  //创建一个数据报类型的套接字

     bind(……);     //邦定公认的短口号

     while(1)        //开始接收客户端的连接

                     //接收和处理客户端的UDP数据报

                   recvfrom(……);

                  if(fork(……)==0)  //创建子进程

                  {

                         process(……);

                         sendto(……);

                   }

         }

除非服务器在处理客户端的请求所用的时间比较长以外,人们实际上很少用这种UDP并发服务器模型的。

 

5、多路复用I/O并发服务器:创建子进程会带来系统资源的大量消耗,为了解决这个问题,采用多路复用I/O模型的并发服务器。采用select函数创建多路复用I/O模型的并发服务器的算法如下:

  初始化(socket,bind,listen);

  while(1)

  {

     设置监听读写文件描述符(FD_*);

     调用select;

     如果是倾听套接字就绪,说明一个新的连接请求建立

     {

           建立连接(accept);

           加入到监听文件描述符中去;

       }

     否则说明是一个已经连接过的描述符

       {

                 进行操作(read或者write);

       }

  }

多路复用I/O可以解决资源限制问题,此模型实际上是将UDP循环模型用在了TCP上面。这也会带了一些问题,如由于服务器依次处理客户的请求,所以可能导致友的客户会等待很久。
 

阻塞
  阻塞,你也许早就听说了。"阻塞"是"sleep" 的科技行话。你可能注意 到前面运行的 listener 程序,它在那里不停地运行,等待数据包的到来。 实际在运行的是它调用 recvfrom(),然后没有数据,因此 recvfrom() 说" 阻塞 (block)",直到数据的到来。
很多函数都利用阻塞。accept() 阻塞,所有的 recv*() 函数阻塞。它们之所以能这样做是因为它们被允许这样做。当你第一次调用 socket() 建立套接字描述符的时候,内核就将它设置为阻塞。如果你不想套接字阻塞,你就要调用函数 fcntl():
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   fcntl(sockfd, F_SETFL, O_NONBLOCK);
  通过设置套接字为非阻塞,你能够有效地"询问"套接字以获得信息。如果你尝试着从一个非阻塞的套接字读信息并且没有任何数据,它不允许阻塞--它将返回 -1 并将 errno 设置为 EWOULDBLOCK。但是一般说来,这种询问不是个好主意。如果你让你的程序在忙等状态查询套接字的数据,你将浪费大量的 CPU 时间。更好的解决之道是用下一章讲的 select() 去查询是否有数据要读进来。
--------------------------------------------------------------------------------
select()--多路同步 I/O
  虽然这个函数有点奇怪,但是它很有用。假设这样的情况:你是个服务器,你一边在不停地从连接上读数据,一边在侦听连接上的信息。没问题,你可能会说,不就是一个accept() 和两个recv() 吗? 这么容易吗,朋友? 如果你在调用accept() 的时候阻塞呢? 你怎么能够同时接受recv() 数据? “用非阻塞的套接字啊!” 不行!你不想耗尽所有的 CPU 吧? 那么,该如何是好?
select() 让你可以同时监视多个套接字。如果你想知道的话,那么它就会告诉你哪个套接字准备读,哪个又准备写,哪个套接字又发生了例外 (exception)。
闲话少说,下面是 select():
int select(int numfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
这个函数监视一系列文件描述符,特别是 readfds、writefds 和 exceptfds。如果你想知道你是否能够从标准输入和套接字描述符 sockfd 读入数据,你只要将文件描述符 0 和 sockfd 加入到集合 readfds 中。参数 numfds 应该等于最高的文件描述符的值加1。在这个例子中,你应该设置该值为 sockfd+1。因为它一定大于标准输入的文件描述符 (0)。当函数 select() 返回的时候,readfds 的值修改为反映你选择的哪个文件描述符可以读。你可以用下面讲到的宏 FD_ISSET() 来测试。在我们继续下去之前,让我来讲讲如何对这些集合进行操作。每个集合类型都是 fd_set。下面有一些宏来对这个类型进行操作:
      FD_ZERO(fd_set *set) – 清除一个文件描述符集合
  FD_SET(int fd, fd_set *set) - 添加fd到集合
  FD_CLR(int fd, fd_set *set) – 从集合中移去fd
  FD_ISSET(int fd, fd_set *set) – 测试fd是否在集合中
最后,是有点古怪的数据结构 struct timeval。有时你可不想永远等待别人发送数据过来。也许什么事情都没有发生的时候你也想每隔96秒在终端上打印字符串"Still Going..."。这个数据结构允许你设定一个时间,如果 时间到了,而 select() 还没有找到一个准备好的文件描述符,它将返回让你继续处理。
数据结构 struct timeval 是这样的:
struct timeval {
   int tv_sec;
   int tv_usec;
   };
只要将tv_sec 设置为你要等待的秒数,将 tv_usec 设置为你要等待的微秒数就可以了。是的,是微秒而不是毫秒。1,000微秒等于1毫秒,1,000 毫秒等于1秒。也就是说,1秒等于1,000,000微秒。为什么用符号"usec" 呢? 字母"u" 很象希腊字母Mu,而Mu 表示"微" 的意思。当然,函数返回的时候 timeout 可能是剩余的时间,之所以是可能,是因为它依赖于你的 Unix 操作系统。哈!我们现在有一个微秒级的定时器!别计算了,标准的Unix 系统的时间片是100毫秒,所以无论你如何设置你的数据结构 struct timeval,你都要等待那么长的时间。

       还有一些有趣的事情:如果你设置数据结构 struct timeval 中的数据为 0,select() 将立即超时,这样就可以有效地轮询集合中的所有的文件描述符。如果你将参数 timeout 赋值为 NULL,那么将永远不会发生超时,即一直等到第一个文件描述符就绪。最后,如果你不是很关心等待多长时间,那么就把它赋为 NULL 吧。


下面的代码演示了在标准输入上等待 2.5 秒:
#define STDIN 0
main()
{
  struct timeval tv;
  fd_set readfds;
      tv.tv_sec = 2;
  tv.tv_usec = 500000;
      FD_ZERO(&readfds);
  FD_SET(STDIN,&readfds);

  select(STDIN+1,&readfds, NULL, NULL,&tv);
      if (FD_ISSET(STDIN,&readfds))
  printf("A key was pressed!\n");
  else
  printf("Timed out.\n");
}
如果你是在一个 line buffered 终端上,那么你敲的键应该是回车 (RETURN),否则无论如何它都会超时。
现在,你可能回认为这就是在数据报套接字上等待数据的方式--你是对的:它可能是。有些 Unix 系统可以按这种方式,而另外一些则不能。

最后一件关于 select() 的事情:如果你有一个正在侦听 (listen()) 的套接字,你可以通过将该套接字的文件描述符加入到 readfds 集合中来看是否有新的连接。


推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
author-avatar
吴佳云怡婷志贤
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有