并发服务器设计
简介
可以同时处理多个客户端的请求,提高服务器的响应
为了弥补循环TCP服务器的缺陷,人们又想出了并
发服务器的模型.
并发服务器的思想是每一个客户机的请求并不由服务器直接处
理,而是服务器创建一个"子进程"[线程]来处理.
并发服务器的设计方案
多进程/多线程的资源消耗
内存资源
每个进程或者线程都会消耗内存资源,客户端越多
消耗的内存资源越多
cpu资源消耗
多进程与多线程需要时间片轮转,切换时需要消耗
资源
默认使用阻塞型io ,多进程/多线程的利用率不高
多进程TCP并发服务器
父进程负责建立连接
子进程负责与客户端的数据交互
每来一个客户端建立连接之后,会创建子进程来
负责数据的交互
流程
服务器
初始化
创建套接字
绑定
监听套接字
创建进程
do_client
子进程资源释放
使用信号进行异步处理
子进程退出时会给父进程发送 SIGCHLD
捕捉SIGCHLD信号,进行处理,需要实现信号处理
函数
信号处理函数
主函数内部
不足
每个进程都需要单独分配内存空间,客户端越多,
则进程越多,就会消耗更多的资源
进程之间的协同需要进程间通讯
文件描述符浪费情况分析
在 fork 之后子进程不需要 监听套接字(sfd),父进
程不需要 客户端套接字(cfd)
将不需要的文件描述符要关闭掉
网络超时检测的方法
<1>设置socket超时
setsockopt
参数
int sockfd:指定要设置/获取属性的套接字文件
描述符;
int level:指定要控制的层次或者协议;
SOL_SOCKET:通用套接字选项; 应用层;
IPPROTO_TCP:TCP选项
IPPROTO_UDP:UDP选项
IPPROTO_IP:IP选项;
int optname:指定要控制的内容; SO_REUSEADDR:端口快速重用; int* optval, 且是bool类型,0为假,非0位真;
void *optval:真实类型根据optname的不同而不同;
socklen_t optlen:optval指针真正指向的数据类型大小
设置允许端口快速重用
在bind之前设置即可
<2>select函数设置超时检测
<3> 超时机制的常用实例。
针对不同的网络异常进行处理
1. 客户端出了问题(死机、重启了、网络断了...)
2. 服务端出了问题(死机、重启了、网络断了、内存耗尽、...)
3. 网络不正常
解决方法
设置心跳包
Keepalive,是TCP中一个可以检测死TCP连接的
机制。
原理
TCP协议会在空闲了一定的时间之后发送数据给
对方
如果对方在一定的时间内没有回应,则表示超
时,撤销连接。
客户端
创建子线程每过1s发送一个心跳包,主线程正常
从键盘输入数据发送。
心跳包类型 MSG msg = {HEART_PACKET,"I am alive!"};
服务器端
心跳包(heart_packet)
服务器
客户端
运行效果
多线程TCP并发服务器
每有一个客户端连接请求过来,就创建一个子线程
使用多线程可以节约一定的资源,减少复杂的进程
间通讯
子线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
子线程分离
int pthread_detach(pthread_t thread);
功能 子线程结束后,自动释放资源
流程
思考:这里为什么需要在堆区创建空间呢?
答案:它主要是在堆区创建空间用于保存每个客
户端的连接套接字。
若是局部变量的话,每来了一个用户就会把之间
的值给覆盖掉。
若是全局变量,也是一样,每来了一个用户就会
把之间的值给覆盖掉。
若是数组,也可以,但是由于不知道连接用户的
个数,数组比较浪费空间
tcp服务器
连接处理
线程处理函数
I/O模型讲解
阻塞(block)IO
当资源不满足条件。 此时进程阻塞。进程休眠,
不会浪费CPU。
最简单,效率低。并且进程最终会阻塞在其中一
个阻塞函数上,而其它的函数没法及时调用。
常见的阻塞I/O read / write fgets/scanf send/recv accept
非阻塞的方式调用(noblock)
问题
需要不断轮询每个函数,浪费cpu资源 。若是没
有数据,则让进程立即返回错误。
错误码
普通文件描述符 EAGAIN
套接字 EAGAIN or EWOULDBLOCK
IO多路复用
同时监控多个文件,用一个单进程同时对多路阻塞的IO进行
控制,降低系统资源的消耗,提高效率
select
poll
epoll
异步信号通知[了解即可]
文件就绪的时候,通过驱动发送信号给应用程序,然后让
应用程序对文件进行操作。
非阻塞IO调用(noblock)
<1>文件描述符的属性修改函数
fcntl函数
file control
提供了对文件描述符的各种控制操作
fcntl函数是由POSIX规范指定的首选方法
功能
对文件描述符的一些属性进行设置或者修改。第三个arg参数是
否使用由,cmd参数的值来决定。
参数
@ fd 操作的文件描述符
@cmd 操作的指令
F_GETFL (get file) arg被忽略不适用
F_SETFL (set file) 通过arg给文件描述符设置
状态标志。
返回值
若是cmd 为F_GETFL 返回一个相应的文件标志。
若是cmd 为F_SETFL 成功返回0,失败返回-1.
例如
./a.out log.txt
修改tcp服务器
修改非阻塞属性
效果