这段时间在做一些web方面开发的事情,用的Nginx+fast-cgi,计划深入看一下Nginx的内部实现和架构,以方便理解和调优。后面准备写一篇有关Nginx介绍和深度解析的文章,要深入理解web服务器的工作原理,网络编程的基本概念和知识不可或缺。这篇文章先对于网络编程中比较容易混淆的几个问题做一个复习和总结,主要参考自《unix网络编程》这本书。
首先,简单总结一下传输层tcp协议的两个琐碎的点。
1、TIME_WAIT状态问题:tcp三次握手建立连接,四次挥手来释放连接,这个大家都熟悉。在释放连接时,主动发起关闭连接的一方的一方会在发送最终确认ack后会有一个TIME_WAIT的状态,如下图所示:
这个状态的持续时间一般是2MSL(maximum segment lifetime),也就是2倍的最长分节生命周期,对于MSL,比较老的数据是30s到2min之间。这个状态存在的理由,有两点:a)、实现终止TCP全双工连接的可靠性(因为客户端的最终ACK可能丢失,从而服务器端会再次发送FIN请求,客户端需要能够应对这种情况的发生,等待新的FIN请求到达后,可以再发送一次最终ACK。旧的ACK丢失到新的FIN到达的时间间隔,最大就是2MSL);b)、允许老的重复分节在网络中消逝(也即,同样的ip、端口建立新的连接时,新的tcp连接和旧的tcp连接的四元组相同,TIME_WAIT状态的引入可以避免将旧的tcp连接网络延迟的数据当做新的tcp连接的数据)
2、四元组,socket套接口用来识别不同tcp连接的一个数据结构,(本地IP地址,本地tcp端口号,远程IP地址,远程tcp端口号),四元组只要有一项不同,就可以认为是不同的连接。其中端口号是一个16bit的数,单机端口数目的最大值是65535。前两天水木上有一个问题,关于多机Nginx做服务器时,最大连接数什么的,具体问题可能要更复杂一些,记不清楚了,但如何判断什么的还是离不开这些基础知识,那个当时我存了合集,有空再去研究一下那个问题。
下面,总结澄清一下阻塞、非阻塞、IO复用、异步、同步、信号驱动的区别和联系,这些是网络编程中比较容易混淆的概念。
总体上讲,应用程序从网络中拿数据,要经历两个阶段:1、数据分组到达网络,并被拷贝到内核的某个缓冲区中,数据报准备好;2、数据从内核缓冲区拷贝至用户态应用程序的缓冲区。基于这两个过程,下面先给出各种IO模式的调用图,最后给出异步、同步IO等区分等。
阻塞I/O:
非阻塞I/O:
I/O复用模型(select、poll等):
信号驱动IO模型:
异步I/O模型:
各种I/O模型的对比,除了异步I/O,其他几种I/O模型等的主要区别在第一阶段也即得知数据报准备好这一过程,第二个阶段(从内核向应用程序缓冲区拷贝)都一样,而异步I/O的两个阶段都与其他模型不一样:
阻塞什么的理解了,那么到底什么是同步I/O、什么是异步I/O呢,Posix.1的响应术语定义如下:
同步I/O:操作引起请求进程阻塞,直到I/O操作完成,因此阻塞I/O、非阻塞I/O(注意,非阻塞只是接受数据的第一阶段,第二阶段还是阻塞的)、I/O复用模型和信号驱动型I/O模型都是同步I/O模型;
异步I/O:不引起请求进程阻塞,因此只有上面两个阶段都不阻塞的异步模型才是符合定义的异步I/O。