Socket的阻塞模式和非阻塞模式

阻塞模式

  Windows套接字在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。

在阻塞模式的套接字上,调用任何一个Windows Sockets API都会耗费不确定的等待时间。图所示,在调用recv()函数时,发生在内核中等待数据和复制数据的过程。

当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。


    Windows套接字程序使用“生产者-消费者”模式来解决上述问题。在程序中,“生产者”读入数据,“消费者”根据需求对读入数据进行处理。通常“生产者”和“消费者”存在于两个线程中,当“生产者”完成读入数据时,使用线程同步机制,例如设置一个事件通知“消费者”,“消费者”接收到这个事件后对读入的数据进行处理。

  当使用socket()函数和WSASocket()函数创建套接字时,默认的套接字都是阻塞的。这意味着当调用Windows Sockets API不能立即完成时,线程处于等待状态,直到操作完成。

并不是所有Windows Sockets API以阻塞套接字为参数调用都会发生阻塞。例如,以阻塞模式的套接字为参数调用bind()、listen()函数时,函数会立即返回。将可能阻塞套接字的Windows Sockets API调用分为以下四种:

1.输入操作

recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。

2.输出操作

send()、sendto()、WSASend()和WSASendto()函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。

3.接受连接

accept()和WSAAcept()函数。以阻塞套接字为参数调用该函数,等待接受对方的连接请求。如果此时没有连接请求,线程就会进入睡眠状态。

4.外出连接

connect()和WSAConnect()函数。对于TCP连接,客户端以阻塞套接字为参数,调用该函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。

  使用阻塞模式的套接字,开发网络程序比较简单,容易实现。当希望能够立即发送和接收数据,且处理的套接字数量比较少的情况下,使用阻塞模式来开发网络程序比较合适。

阻塞模式套接字的不足表现为,在大量建立好的套接字线程之间进行通信时比较困难。当使用“生产者-消费者”模型开发网络程序时,为每个套接字都分别分配一个读线程、一个处理数据线程和一个用于同步的事件,那么这样无疑加大系统的开销。其最大的缺点是当希望同时处理大量套接字时,将无从下手,其扩展性很差。

非阻塞模式
  把套接字设置为非阻塞模式,即通知系统内核:在调用Windows Sockets API时,不要让线程睡眠,而应该让函数立即返回。在返回时,该函数返回一个错误代码。图所示,一个非阻塞模式套接字多次调用recv()函数的过程。前三次调用recv()函数时,内核数据还没有准备好。因此,该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时,数据已经准备好,被复制到应用程序的缓冲区中,recv()函数返回成功指示,应用程序开始处理数据。

  当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式。Linux下的函数是:fcntl().
    套接字设置为非阻塞模式后,在调用Windows Sockets API函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。

    需要说明的是并非所有的Windows Sockets API在非阻塞模式下调用,都会返回WSAEWOULDBLOCK错误。例如,以非阻塞模式的套接字为参数调用bind()函数时,就不会返回该错误代码。当然,在调用WSAStartup()函数时更不会返回该错误代码,因为该函数是应用程序第一调用的函数,当然不会返回这样的错误代码。

    要将套接字设置为非阻塞模式,除了使用ioctlsocket()函数之外,还可以使用WSAAsyncselect()和WSAEventselect()函数。当调用该函数时,套接字会自动地设置为非阻塞方式。

  由于使用非阻塞套接字在调用函数时,会经常返回WSAEWOULDBLOCK错误。所以在任何时候,都应仔细检查返回代码并作好对“失败”的准备。应用程序连续不断地调用这个函数,直到它返回成功指示为止。上面的程序清单中,在While循环体内不断地调用recv()函数,以读入1024个字节的数据。这种做法很浪费系统资源。

    要完成这样的操作,有人使用MSG_PEEK标志调用recv()函数查看缓冲区中是否有数据可读。同样,这种方法也不好。因为该做法对系统造成的开销是很大的,并且应用程序至少要调用recv()函数两次,才能实际地读入数据。较好的做法是,使用套接字的“I/O模型”来判断非阻塞套接字是否可读可写。

    非阻塞模式套接字与阻塞模式套接字相比,不容易使用。使用非阻塞模式套接字,需要编写更多的代码,以便在每个Windows Sockets API函数调用中,对收到的WSAEWOULDBLOCK错误进行处理。因此,非阻塞套接字便显得有些难于使用。

    但是,非阻塞套接字在控制建立的多个连接,在数据的收发量不均,时间不定时,明显具有优势。这种套接字在使用上存在一定难度,但只要排除了这些困难,它在功能上还是非常强大的。通常情况下,可考虑使用套接字的“I/O模型”,它有助于应用程序通过异步方式,同时对一个或多个套接字的通信加以管理。

时间: 2024-10-21 20:05:57

Socket的阻塞模式和非阻塞模式的相关文章

阻塞赋值和非阻塞赋值

在always语句块中,verilog语言支持两种类型的赋值:阻塞赋值和非阻塞赋值.阻塞赋值使用"="语句:非阻塞赋值使用"<="语句.注意,千万不要将这两种赋值方法与assign赋值语句混淆起来,assign赋值语句根本不允许出现在always语句块中.      位于begin/end块内的多条阻塞赋值语句是串行执行的,这一点同标准的程序设计语言是相同的.但是多条非阻塞赋值语句却是并行执行的,这些非阻塞赋值语句都会在其中任何一条语句执行完成之前开始执行.这

node.js回调函数之阻塞调用与非阻塞调用_node.js

首先,node.js作为javascript运行平台,它采用了事件驱动和异步编程的方式,通过事件注册和异步函数,开发人员可以提高资源利用率,服务器的性能也能得到改善.其次,对于前端人来说,node.js作为js的运行平台,我们可以通过编写系统级或者服务器端的javascript代码交给node.js来执行,让我们前端人也能作用于后台,相比之下,浏览器端的javascript代码在运行时会受到各种安全性的限制,对客户系统的操作有限,而node.js则是一个全面的后台运行时,为javascript提

Oracle归档模式和非归档模式

Oracle归档模式和非归档模式 解释归档和非归档模式之间的不同和它们各自的优缺点? 答:归档模式是指可以备份所有的数据库transactions并恢复到任意一个时间点.         非归档模式则相反,不能恢复到任意一个时间点.         但是非归档模式可以带来数据库性能上的少许提高. 记忆方式:归档模式>热备份>恢复任意时间点>性能少许下降                       非归档模式>冷备份>恢复完全备份>性能少许提高 一.查看oracle数据库

将oracle设为归档模式和非归档模式启动的方法

将oracle设为归档模式和非归档模式的方法 先手动建立归档目录,在调整oracle启动方式 将oracle设为归档模式 手工在主服务器创建归档目录 $cd $ORACLE_BASE            #$ORACLE_BASE 所指向的目录是/opt/oracle/,可参看ORACLE用户环境变量的具体设置 $mkdir -p oradata/szdb/archive   # szdb的目录名称根据数据库的SID号进行指定,其实这个目录可以任意指定 $mkdir -p oradata/sz

阻塞模式和非阻塞模式

好文得转~~~ 何为阻塞?  从该网络通讯过程来理解一下何为阻塞 :  在以上过程中若连接还没到来,那么 accept 会阻塞 , 程序运行到这里不得不挂起, CPU 转而执行其他线程.  在以上过程中若数据还没准备好, read 会一样也会阻塞.  阻塞式网络 IO 的特点:多线程处理多个连接.每个线程拥有自己的栈空间并且占用一些 CPU 时间.每个线程遇到外部为准备好的时候,都会阻塞掉.阻塞的结果就是会带来大量的进程上下文切换.且大部分进程上下文切换可能是无意义的.比如假设一个线程监听一个端

oracle日志操作模式(归档模式和非归档模式的利与弊)_oracle

笔者今天就谈谈自己对这两种操作模式的理解,并且给出一些可行的建议,跟大家一起来提高Oracle数据库的安全性. 一.非归档模式的利与弊. 非归档模式是指不保留重做历史的日志操作模式,只能够用于保护例程失败,而不能够保护介质损坏.如果数据库采用的是日志操作模式的话,则进行日志切换时,新的日志会直接覆盖原有日志文件的内容,不会保留原有日志文件中的数据. 这么说听起来可能比较难理解.笔者举一个简单的例子,就会清楚许多.如现在Oracle数据库中有四个日志组,日志序列号分别为11. 12.13.14.当

[正则表达式]贪婪模式与非贪婪模式_正则表达式

复制代码 代码如下: /**     **   author: site120     **   function : get script part from html document     **/     var loadJs = function(str , delayTime)      {          var delayTime = delayTime || 100;          var regExp_scriptTag = new RegExp("<\\s*sc

非阻塞IO模式原理

与阻塞模式对应的另一种模式叫非阻塞IO模式,在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况.从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍历到此通道有字节要写并往socket写数据,B机器一条线程遍历到此通道有字节要读,交给另外一条线程对socket读数据,处理完又把通道设置为写事件,遍历线程遍历到此通道有字节要写,又往socket写数据传往A机器,不断往下循环此操作直到完成通信.这个过程每台机器都有两类主要线程,一类是负责逻辑处理且

浅谈socket同步和异步、阻塞和非阻塞、I/O模型_php技巧

在进行网络编程时,常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式 同步/异步主要针对C端: 同步:c端发出一个功能调用时,在没有得到结果之前,c端死等结果 例如:普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:c端一个异步过程调用发出后,调用者不会立刻得到结果.实际处理这个调用的部件在完成后,通过状态.通知和回调来通知调用者. 例如:ajax请求(异步): 事件触发->服务