网络编程之 Socket的模式(一)
1. 阻塞/非阻塞 对于网络编程而言,Socket模式是开发者必须明确的一个问题。对于Socket的操作,可以分为阻塞模式和非阻塞模式两种。在两种不同模式下,同一个Socket函数的表现可能完全不同,所以必须引起开发者的注意。
在解释阻塞(blocking)和非阻塞(non-blocking)之前,先看另外一对相对的词语,"同步"(synchronous)和"异步"(asynchronous)。同步和异步指的是两个模块间的消息交互方式。在程序中,模块之间的所有关系都是通过不同的函数去体现的。我们知道所有的函数都可以存在返回值,如果模块A调用模块B的f()函数时,得到的参数返回值能够代表业务上所需要的结果,则可以称此函数f()为同步函数,模块A和模块B就此消息流程的过程是同步的。否则则称为异步。
上面的解释太过于绕口令了,举个简单的例子:
模块A为应用界面,模块B为网络通讯模块,A通过B同远端的模块C进行实时通讯。模块B存在接口函数f(),函数f()能够保证网络通讯的数据完整性和正确性,并通过返回值bool量来表示成功失败(成功表示C端接收到完整的模块B所发送的数据,而失败则相反)。如果模块A调用模块B的f()函数的目的,只是为了把数据发送到C端,则在此情况下,我们可以把f()函数所代表的动作看作是一个同步动作。而如果模块A通过模块B的f()函数发送的数据具有某种实际意义,模块A期望的是得到所发送数据的真实响应结果,如A发送的数据是用来操作C促发某个动作,如操作一个数据库,或者前端的某个机械部件,A希望得到此动作的一个结果。则模块B提供的f()函数就不能被看作是一个同步的接口,因为f()函数的返回值只表示网络数据发送的成功与失败。如果A需要得到真实的操作结果,必须对B进行其他调用。A发送消息并得到其想要的对应结果的过程被分割成了两个部分,因此可以被看作是一个异步过程。
抛开上面的同步和异步,跑的更远一些。我们都知道编程当中,在对现实世界的业务组织上,模块的思想是非常重要的,每一模块都只专注于自己需要实现的事情,彼此之间互不影响,模块之间通过有限的接口来实现互动。通过对模块的合理规划,可以使代码具有更好的组织性和维护性。
同时我们也知道,在计算机所提供实现编程方式里,能够使用的技术只有函数调用,线程,进程。当一个函数的调用结果能够满足其调用者的调用目的时,这个函数可以被认为是一个同步函数。反之则可认为是一个异步函数。当函数f()为异步时,调用者想要知道f()函数的真实结果,有两种选择: 第一,调用另外一个函数去查询结果,这种方式可以称为查询,定期的查询则变成轮询。第二,存在一个函数能够主动的通知调用者,真实的调用结果,这种方式可以被称为回调函数方式(或者类似的称呼“通知”、“事件”)。
存在被调方对调用方的主动通知的模块,我们可以认为这是一个主动对象模块。主动对象模块在实现上有两个特点,缺一不可。第一,必须依赖于多线程,或者多进程。第二,存在回调函数接口。要注意的是,存在多线程的模块未必是可以成为是主动对象模块,因为可能不满足第二个条件。同主动对象模块相对应的,就是被动对象模块了,被动对象模块只存在被调用的接口。
有些扯远了,让我们回到Socket编程上。如果把操作系统看成一个模块,而把所有的应用程序看成是另外一个模块的话,Socket函数可以被看成是模块操作系统对其他应用模块提供的一组接口函数,而这组函数有阻塞和非阻塞两种模式。在Linux设计中,目录、文件、Socket、设备等一切都可以看成是I/O,对这些设备、文件等的读写可以被认为是I/O操作。阻塞和非阻塞就是针对I/O操作而言的,在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,操作函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。这就是阻塞和非阻塞的区别,仅此而已。非阻塞式模式可能在I/O操作没有完成的情况下返回,太奇怪了,要它干么? 其实不然,非阻塞式I/O的这种方式,实际上就是上面讲述的结果查询或轮询方法。
int recv(int sockfd, void *buf, int len, unsigned int flags);阻塞模式下,recv()函数成功时返回实际读入缓冲的数据的字节数。错误的时候返回-1, 同时设置 errno。
上面讲了TCP协议的情况。再来看看UDP协议下的情况,我们都知道UDP协议是不可靠的数据传输协议,发送端并不关心接受端是否能够正确的接受到所发送的数据,而只是尽可能的尽快的发送数据。因此在UDP协议下,操作系统理论上是可以不维护缓冲的,发送时可以只是简单的把用户层的数据复制一份,直接交给网卡去发送。而接收时,把网卡数据拷贝进入系统缓冲,如果用户没有从系统缓冲中及时拿走数据,后来的数据可以覆盖先前的数据。所有对于UDP协议,应用层在发送时,应该考虑的是尽量发送,而在接收时,应该考虑的是及时接受。因此阻塞式和非阻塞并非设计时需要考虑的要点。
(版权所有,转载时请注明作者和出处http://blog.csdn.net/arau_sh/article/details/12757095)