五种服务器网络编程模型
首先来看看 Linux 上可以使用的 I/O 模型,下图是基本 Linux I/O 模型的简单矩阵,了解这些 I/O 相关知识能有助于理解后面的网络模型。
1.同步阻塞迭代模型
首先介绍同步阻塞迭代模型,它的核心代码如下:
bind(srvfd, ...);
listen(srvfd, ...);
for(;;){
clifd = accept(srvfd, ...); //开始接受客户端来的连接
read(clifd,buf, ...); //从客户端读取数据,一直阻塞,直到读取到数据才返回
dosomthingonbuf(buf);
write(clifd, buf, ...) //发送数据到客户端
}
作为最简单直接的一种 IO模型,它存在以下弊端:
1、如果没有客户端的连接请求,进程会阻塞在 accept() 里,然后被 CPU 挂起。
2、在与客户端建立好连接后,通过 read() 从客户端接受数据,而客户端合适发送数据过来是不可控的。如果客户端迟迟不发生数据过来,缓冲区没有数据可读,那么程序同样会**阻塞**在 read() 中,此时,如果另外的客户端来尝试连接时,程序都不会响应。
3、同样的道理,write() 也会使得程序出现阻塞(例如:客户端接收数据异常缓慢,服务器端发生数据速度也会很慢,导致写缓冲区满,write() 则一直阻塞知道将数据成功写入缓冲区则返回)。
2.多进程并发模型
再来介绍一种可以同时接受多个连接请求的模型,它在同步阻塞迭代模型的基础上,引入了多进程。
核心代码:
bind(srvfd, ...); //绑定套接字的本地地址
listen(srvfd, ...); //监听
for(;;){
clifd = accept(srvfd, ...); //父进程开始接受来自客户端连接
ret = fork(); //申请进程
switch (ret)
{
case -1 :
do_err_handler();
break;
case 0 : // 返回 0 则为子进程
client_handler(clifd);
break;
default : // 返回正整数则为父进程,返回值为子进程 ID
close(clifd);
continue;
}
}
// 子进程处理逻辑
void client_handler(clifd){
read(clifd, buf, ...); //从客户端读取数据
dosomthingonbuf(buf);
write(clifd, buf, ...) //发送数据到客户端
}
在单个进程中,也同样存在第一个模型的问题,一个进程同时只能处理一个请求,该方法通过“主进程负责监听,而多个子进程负责处理”的方式来获得并发,此模式属于fork and execute模式,性能也非常低下。
3、多线程并发模型
在多进程并发模型中ÿ