Linux网络编程(事件驱动模式)

Linux网络编程(事件驱动模式)


2024年4月20日发(作者:)

前言

前言

事件驱动为广大的程序员所熟悉,其最为人津津乐道的是在图形化界面编程中的应用;

事实上,在网络编程中事件驱动也被广泛使用,并大规模部署在高连接数高吞吐量的服务器

程序中,如 http 服务器程序、ftp 服务器程序等。相比于传统的网络编程方式,事件驱动

能够极大的降低资源占用,增大服务接待能力,并提高网络传输效率。

关于本文提及的服务器模型,搜索网络可以查阅到很多的实现代码,所以,本文将不拘

泥于源代码的陈列与分析,而侧重模型的介绍和比较。使用 libev事件驱动库的服务器模型

将给出实现代码。

本文涉及到线程 / 时间图例,只为表明线程在各个 IO 上确实存在阻塞时延,但并不保

证时延比例的正确性和 IO 执行先后的正确性;另外,本文所提及到的接口也只是笔者熟悉

的 Unix/Linux 接口,并未推荐 Windows 接口,读者可以自行查阅对应的 Windows 接口。

阻塞型的网络编程接口

几乎所有的程序员第一次接触到的网络编程都是从 listen()、send()、recv() 等接口开

始的。使用这些接口可以很方便的构建服务器 / 客户机的模型。

我们假设希望建立一个简单的服务器程序,实现向单个客户机提供类似于“一问一答”

的内容服务。

图 1. 简单的一问一答的服务器

1.

简单的一问一答的服务器 /

简单的一问一答的服务器

/ 客户机模型

/

客户机模型

客户机模型

我们注意到,大部分的 socket 接口都是阻塞型的。所谓阻塞型接口是指系统调用(一

般是 IO 接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超

时出错时才返回。

实际上,除非特别指定,几乎所有的 IO 接口 ( 包括 socket 接口 ) 都是阻塞型的。

这给网络编程带来了一个很大的问题,如在调用 send() 的同时,线程将被阻塞,在此期间,

线程将无法执行任何运算或响应任何的网络请求。这给多客户机、多业务逻辑的网络编程带

来了挑战。这时,很多程序员可能会选择多线程的方式来解决这个问题。

多线程的服务器程序

多线程的服务器程序

应对多客户机的网络应用,最简单的解决方式是在服务器端使用多线程(或多进程)。

多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接

的阻塞都不会影响其他的连接。

具体使用多进程还是多线程,并没有一个特定的模式。传统意义上,进程的开销要远远

大于线程,所以,如果需要同时为较多的客户机提供服务,则不推荐使用多进程;如果单个

服务执行体需要消耗较多的 CPU 资源,譬如需要进行大规模或长时间的数据运算或文件访

问,则进程较为安全。通常,使用 pthread_create () 创建新线程,fork() 创建新进程。

我们假设对上述的服务器 / 客户机模型,提出更高的要求,即让服务器同时为多个客户

机提供一问一答的服务。于是有了如下的模型。

图 2. 多线程的服务器模型

2.

多线程的服务器模型

多线程的服务器模型

在上述的线程 / 时间图例中,主线程持续等待客户端的连接请求,如果有连接,则创建

新线程,并在新线程中提供为前例同样的问答服务。

很多初学者可能不明白为何一个 socket 可以 accept 多次。实际上,socket 的设计者

可能特意为多客户机的情况留下了伏笔,让 accept() 能够返回一个新的 socket。下面是

accept 接口的原型:

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

输入参数 s 是从 socket(),bind() 和 listen() 中沿用下来的 socket 句柄值。执行

完 bind() 和 listen() 后,操作系统已经开始在指定的端口处监听所有的连接请求,如果

有请求,则将该连接请求加入请求队列。调用 accept() 接口正是从 socket s 的请求队列

抽取第一个连接信息,创建一个与 s 同类的新的 socket 返回句柄。新的 socket 句柄即是

后续 read() 和 recv() 的输入参数。如果请求队列当前没有请求,则 accept() 将进入阻

塞状态直到有请求进入队列。

上述多线程的服务器模型似乎完美的解决了为多个客户机提供问答服务的要求,但其实

并不尽然。如果要同时响应成百上千路的连接请求,则无论多线程还是多进程都会严重占据

系统资源,降低系统对外界响应效率,而线程与进程本身也更容易进入假死状态。

很多程序员可能会考虑使用“线程池线程池”或“连接池连接池”。“线程池”旨在减少创建和销毁

线程池连接池

线程的频率,其维持一定合理数量的线程,并让空闲的线程重新承担新的执行任务。“连接

池”维持连接的缓存池,尽量重用已有的连接、减少创建和关闭连接的频率。这两种技术都

可以很好的降低系统开销,都被广泛应用很多大型系统,如 websphere、tomcat 和各种数据

库等。


发布者:admin,转转请注明出处:http://www.yc00.com/news/1713550487a2271288.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信