文档介绍:该【阻塞和非阻塞通信 】是由【游园会】上传分享,文档一共【10】页,该文档可以免费在线阅读,需要了解更多关于【阻塞和非阻塞通信 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。同步:函数没有执行完不返回,线程被挂起;
堵塞:没有收完数据函数不返回,线程也被挂起;
异步:函数马上返回,通过大事或是信号通知调用者; 非堵塞:函数马上返回,通过 select 通知调用者
同步和堵塞是比较简洁弄明白其含义的,但在实际编程过程中,异步与非堵塞的概念却并不能直观地区分于“通过大事或是信号通知调用者”与“通过 select 通知调用者”这种字面解释。堵塞通信意味着通信方法在尝试访问套接字或者读写数据时堵塞了对套接字的访问。在 JDK 之前,绕过堵塞限制的方法是无限制地使用线程,但这样常常会造成大量的线程开销,对系统的性能和可伸缩性产生影响。 包转变了这种状况,允许效劳器有效地使用 I/O 流,在合理的时间内处理所效劳的客户恳求。
非堵塞通信,这个过程就像我所宠爱说的“为所欲为”那样。这个过程就是发送和读取任何能够发送/读取的东西。假设没有可以读取的东西,它就中止读操作,做其他的事情直到能够读取为止。当发送数据时,该过程将试图发送全部的数据,但返回实际发送出的内容。可能是全部数据、局部数据或者根本没有发送数据。
堵塞与非堵塞相比确实有一些优点,特别是遇到错误掌握问题的时候。在堵塞套接字通信中, 假设消灭错误,该访问会自动返回标志错误的代码。错误可能是由于网络超时、套接字关闭或者任何类型的 I/O 错误造成的。在非堵塞套接字通信中,该方法能够处理的唯一错误是网络超时。为了检测使用非堵塞通信的网络超时,需要编写略微多一点的代码,以确定自从上一次收到数据以来已经多长时间了。
哪种方式更好取决于应用程序。假设使用的是同步通信,假设数据不必在读取任何数据之前处理的话,堵塞通信更好一些,而非堵塞通信则供给了处理任何已经读取的数据的时机。而异步通信,如 IRC 和谈天客户机则要求非堵塞通信以避开冻结套接字。
Java 中的堵塞和非堵塞 IO 包各自的优劣思考
NIO 设计背后的基石:反响器模式,用于大事多路分别和分派的体系构造模式 。反响器〔Reactor〕:用于大事多路分别和分派的体系构造模式〔利用线程池〕
通常的,对一个文件描述符指定的文件或设备, 有两种工作方式: 堵塞与非堵塞。所谓堵塞
方式的意思是指, 当试图对该文件描述符进展读写时, 假设当时没有东西可读,或者临时不行写, 程序就进入等待状态, 直到有东西可读或者可写为止。而对于非堵塞状态, 假设没有东西可读, 或者不行写, 读写函数马上返回, 而不会等待。
一种常用做法〔传统做法〕是:每建立一个 Socket 连接时,同时创立一个线程对该 Socket 进展单独通信〔承受堵塞的方式通信〕。这种方式具有很高的响应速度,并且掌握起来也 很简洁,在连接数较少的时候格外有效,但是假设对每一个连接都产生一个线程的无疑是对系统资源的一种铺张,假设连接数较多将会消灭资源缺乏的状况。
另一种较高效的做法是:效劳器端保存一个 Socket 连接列表,然后对这个列表进展 轮询 , 假设觉察某个 Socket 端口上有数据可读时〔读就绪〕,则调用该 socket 连接的相应读操作; 假设觉察某个 Socket 端口上有数据可写时〔写就绪〕,则调用该 socket 连接的相应写操 作;假设某个端口的 Socket 连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用效劳器资源,效率得到了很大提高。
传统的堵塞式 IO,每个连接必需要开一个线程来处理,并且没处理完线程不能退出。
非堵塞式 IO,由于基于反响器模式,用于大事多路分别和分派的体系构造模式,所以可以利用线程池来处理。大事来了就处理,处理完了就把线程归还。而传统堵塞方式不能使用线程池来处理,假设当前有10000 个连接,非堵塞方式可能用1000 个线程的线程池就搞定了, 而传统堵塞方式就需要开 10000 个来处理。假设连接数较多将会消灭资源缺乏的状况。非堵塞的核心优势就在这里。
为什么会这样,下面就对他们做进一步细致具体的分析:
首先,我们来分析传统堵塞式 IO 的瓶颈在哪里。在连接数不多的状况下,传统 IO 编写简洁便利使用。但是随着连接数的增多,问题传统 IO 就不行了。由于前面说过,传统IO 处
理每个连接都要消耗一个线程,而程序的效率当线程数不多时是随着线程数的增加而增加,
但是到肯定的数量之后,是随着线程数的增加而削减这。里我们得出结论,传统堵塞式 IO
的瓶颈在于不能处理过多的连接。
然后,非堵塞式 IO 的消灭的目的就是为了解决这个瓶颈。而非堵塞式 IO 是怎么实现的呢? 非堵塞 IO 处理连接的线程数和连接数没有联系,也就是说处理 10000 个连接非堵塞IO 不需要 10000 个线程,你可以用 1000 个也可以用 2025 个线程来处理。由于非堵塞 IO 处理连接是异步的。当某个连接发送恳求到效劳器,效劳器把这个连接恳求当作一个恳求“大事“, 并把这个“大事“安排给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行 完就把线程归还。这样一个线程就可以异步的处理多个大事。而堵塞式 IO 的线程的大局部 时间都铺张在等待恳求上了。
转载声明:本文转自 ://
===================================================================
========
非堵塞 Socoket 编程
在互联网相当普及的今日,在互联网上谈天对很多“网虫”来说已经是家常便饭了。谈天室程序可以说是网上最简洁的多点通信程序。谈天室的实现方法有很多,但都是利用所谓的“多
用户空间”来对信息进展交换,具有典型的多路 I/O 的架构。一个简洁的谈天室, 从程序员的观点来看就是在多个 I/O 端点之间实现多对多的通信。其架构如图一所示。这样的实现在用户的眼里就是谈天室内任何一个人输入一段字符之后,其他用户都可以得到这一句话。这 种“多用户空间”的架构在其他多点通信程序中应用的格外广泛,其核心就是多路 I/O 通信。多路 I/O 通信又被称为 I/O 多路复用〔I/OMultiplexing〕一般被使用在以下的场合:
1、客户程序需要同时处理交互式的输入和同效劳器之间的网络连接时需要处理 I/O 多路复用问题;
2、客户端需要同时对多个网络连接作出反响〔这种状况很少见〕;
3、TCP 效劳器需要同时处理处于监听状态和多个连接状态的 socket;
4、效劳器需要处理多个网络协议的 socket;
5、效劳器需要同时处理不同的网络效劳和协议。
谈天室所需要面对的状况正是第一和第三两种状况。我们将通过在 TCP/IP 协议之上建立一个功能简洁的谈天室让大家更加了解多路 I/O 以及它的实现方法。我们要争论的谈天室功能格外简洁, 感兴趣的朋友可以将其功能扩展, 进展成一个功能比较完整的谈天室, 如加上用户认证, 用户昵称, 隐秘信息, semote 等功能.
首先它是一个 client/server 构造的程序, 首先启动 server, 然后用户使用 client 进展连接. client/server 构造的优点是速度快, 缺点是当 server 进展更时,client 也必需更.
网络初始化
首先是初始化 server, 使 server 进入监听状态: (为了简洁起见,以下引用的程序与实际程序略有出入, 下同)
sockfd = socket( AF_INET,SOCK_STREAM, 0);
// 首先建立一个 socket, 族为 AF_INET, 类型为 SOCK_STREAM.
// AF_INET = ARPA Internet protocols 即使用 TCP/IP 协议族
// SOCK_STREAM 类型供给了挨次的, 牢靠的, 基于字节流的全双工连接.
// 由于该协议族中只有一个协议, 因此第三个参数为 0
bind( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr));
// 再将这个 socket 与某个地址进展绑定.
// serv_addr 包括 sin_family = AF_INET 协议族同 socket
// = htonl( INADDR_ANY) server 所承受的全部其他
// 地址恳求建立的连接.
// sin_port = htons( SERV_TCP_PORT) server 所监听的端口
// 在本程序中, server 的 IP 和监听的端口都存放在 config 文件中.
listen( sockfd, MAX_CLIENT);
// 地址绑定之后, server 进入监听状态.
// MAX_CLIENT 是可以同时建立连接的 client 总数. server 进入 listen 状态后, 等待 client 建立连接。Client 端要建立连接首先也需要初始化连接:
sockfd = socket( AF_INET,SOCK_STREAM,0));
// 同样的, client 也先建立一个 socket, 其参数与 server 一样. connect( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr));
// client 使用 connect 建立一个连接.
// serv_addr 中的变量分别设置为:
// sin_family = AF_INET 协议族同 socket
// = inet_addr( SERV_HOST_ADDR) 地址为 server
// 所在的计算机的地址.
// sin_port = htons( SERV_TCP_PORT) 端口为 server 监听的端口.
当 client 建立连接的恳求被送到 Server 端时, server 使用 accept 来承受该连接:
accept( sockfd, (struct sockaddr*)&cli_addr, &cli_len);
// 在函数返回时, cli_addr 中保存的是该连接对方的信息
// 包括对方的 IP 地址和对方使用的端口.
// accept 返回一个的文件描述符.
在 server 进入 listen 状态之后, 由于已有多个用户在线,所以程序需要同时对这些用户进展操作,并在它们之间实现信息交换。这在实现上称为 I/O 多路复用技术。多路复用一般有以下几种方法:
非堵塞通信方法:将文件管道通过 fcntl设为非堵塞通信方式,每隔一端时间对他们实行一次轮询,以推断是否可以进展读写操作。这种方式的缺点是费用太高,大局部资源铺张在轮询上。
子进程方法:应用多个子进程,每一个对一个单工堵塞方式通信。全部子进程通过 IPC 和父进程进展通信。父进程掌管全部信息。这种方式的缺点是实现简单,而且由于 IPC 在各个操作系统平台上并不完全全都,会导致可移植性降低。
信号驱动〔SIGIO〕的异步 I/O 方法:首先,异步 I/O 是基于信号机制的,并不行靠。其次单一的信号缺乏以供给更多的信息来源。还是需要关心以其他的手段,实现上有很高的难度。
select 方法:在 BSD 中供给了一种可以对多路 I/O 进展堵塞式查询的方法——select。它供给同时对多个 I/O 描述符进展堵塞式查询的方法,利用它,我们可以很便利的实现多路复用。依据统一 UNIX 标准的协议,POSIX 也承受了这种方法,因此,我们可以在大多数操作系统中使用 select 方法。
使用特地的 I/O 多路复用器:在“UNIX? SYSTEM V Programmer”s Guide: STREAMS”一书中具体的说明白构造和使用多路复用器的方法。这里就不再详述了。
我们下面分别争论多路 I/O 的两种实现方法:
1. 非堵塞通信方法
对一个文件描述符指定的文件或设备, 有两种工作方式: 堵塞与非堵塞。所谓堵塞方式的意思是指, 当试图对该文件描述符进展读写时, 假设当时没有东西可读,或者临时不行写, 程 序就进入等待状态, 直到有东西可读或者可写为止。而对于非堵塞状态, 假设没有东西可读, 或者不行写, 读写函数马上返回, 而不会等待。缺省状况下, 文件描述符处于堵塞状态。在实现谈天室时, server 需要轮番查询与各 client 建立的 socket,一旦可读就将该 socket 中的字符读出来并向全部其他 client 发送。并且, server 还要随时查看是否有的 client 试
图建立连接,这样, 假设 server 在任何一个地方堵塞了, 其他 client 发送的内容就会受到 影响,得不到效劳器的准时响应。 client 试图建立连接也会受到影响。所以我们在这里不能使用缺省的堵塞的文件工作方式,而需要将文件的工作方式变成非堵塞方式。在UNIX下, 函数 fcntl可以用来转变文件 I/O 操作的工作方式,函数描述如下:
fcntl( sockfd, F_SETFL, O_NONBLOCK);
// sockfd 是要转变状态的文件描述符.
// F_SETFL 说明要转变文件描述符的状态
// O_NONBLOCK 表示将文件描述符变为非堵塞的.
为了节约篇幅我们使用自然语言描述谈天室 server :
while ( 1)
if 有连接 then 建立并记录该连接; for ( 全部的有效连接)
begin
if 该连接中有字符可读 then begin
读入字符串;
for ( 全部其他的有效连接) begin
将该字符串发送给该连接; end;
end; end; end.
由于推断是否有连接, 是否可读都是非堵塞的, 因此每次推断,不管有还是没有, 都会马上返回. 这样,任何一个 client 向 server 发送字符或者试图建立连接,都不会对其他client 的活动造成影响。
对 client 而言, 建立连接之后, 只需要处理两个文件描述符, 一个是建立了连接的 socket 描述符, 另一个是标准输入. 和 server 一样, 假设使用堵塞方式的话,很简洁由于其中一个临时没有输入而影响另外一个的读入.. 因此将它们都变成非堵塞的,然后 client 进展如下动
作:
while ( 不想退出) begin
if ( 与 server 的连接有字符可读)
begin
从该连接读入, 并输出到标准输出上去. End;
if ( 标准输入可读)
Begin
从标准输入读入, 并输出到与 server 的连接中去.
End; End.
上面的读写分别调用这样两个函数:
read( userfd[i], line, MAX_LINE);
// userfd[i] 是指第 i 个 client 连接的文件描述符.
// line 是指读出的字符存放的位置.
// MAX_LINE 是一次最多读出的字符数.
// 返回值是实际读出的字符数.
write( userfd[j], line, strlen( line));
// userfd[j] 是第 j 个 client 的文件描述符.
// line 是要发送的字符串.
// strlen( line) 是要发送的字符串长度.
分析上面的程序可以知道, 不管是 server 还是 client, 它们都不停的轮番查询各个文件描述符, 一旦可读就读入并进展处理. 这样的程序, 不停的在执行, 只要有 CPU 资源, 就不会放过。因此对系统资源的消耗格外大。server 或者 client 单独执行时,CPU 资源的 98% 左右都被其占用。极大的消耗了系统资源。
select 方法因此,虽然我们不期望在某一个用户没有反响时堵塞其他的用户,但我们却应当在没有任何用户有反响的状况之下停顿程序的运行,让出抢占的系统资源,进入堵塞状态。有没有这种方法呢?现在的 UNIX 系统中都供给了 select 方法,具体实现方式如下:
select 方法中, 全部文件描述符都是堵塞的. 使用 select 推断一组文件描述符中是否有一个可读(写), 假设没有就堵塞, 直到有一个的时候就被唤醒. 我们先看比较简洁的 client 的实现:
由于 client 只需要处理两个文件描述符, 因此, 需要推断是否有可读写的文件描述符只需要参加两项:
FD_ZERO( sockset);
// 将 sockset 清空
FD_SET( sockfd, sockset);
// 把 sockfd 参加到 sockset 集合中
FD_SET( 0, sockset);
// 把 0 (标准输入) 参加到 sockset 集合中然后 client 的处理如下:
while ( 不想退出)
select( sockfd+1, &sockset, NULL, NULL, NULL);
// 此时该函数将堵塞直到标准输入或者 sockfd 中有一个可读为止
// 第一个参数是 0 和 sockfd 中的最大值加一
// 其次个参数是读集, 也就是 sockset
// 第三, 四个参数是写集和特别集, 在本程序中都为空
// 第五个参数是超时时间, 即在指定时间内仍没有可读, 则出错
// 并返回. 当这个参数为 NULL 时, 超时时间被设置为无限长.
// 当 select 由于可读返回时, sockset 中包含的只是可读的
// 那些文件描述符.
if ( FD_ISSET( sockfd, &sockset))
// FD_ISSET 这个宏推断 sockfd 是否属于可读的文件描述符从 sockfd 中读入, 输出到标准输出上去.
}
if ( FD_ISSET( 0, &sockset))
// FD_ISSET 这个宏推断 sockfd 是否属于可读的文件描述符从标准输入读入, 输出到 sockfd 中去.
}
重设置 sockset. (马上 sockset 清空, 并将 sockfd 和 0 参加)
}
下面看 server 的状况:
设置 sockset 如下:
FD_ZERO( sockset); FD_SET( sockfd, sockset); for ( 全部有效连接) FD_SET( userfd[i], sockset);
}
maxfd = 最大的文件描述符号 + 1; server 处理如下:
while ( 1)
select( maxfd, &sockset, NULL, NULL, NULL); if ( FD_ISSET( sockfd, &sockset))
// 有连接
建立连接, 并将该连接描述符参加到 sockset 中去了.
}
for ( 全部有效连接)
if ( FD_ISSET ( userfd[i], &sockset))
// 该连接中有字符可读
从该连接中读入字符, 并发送到其他有效连接中去.
}
}
重设置 sockset;
}
性能比较
由于承受 select 机制, 因此当没有字符可读时, 程序处于堵塞状态,最小程度的占用 CPU 资源, 在同一台机器上执行一个 server 和假设干个 client 时, 系统负载只有 左右, 而承受原来的非堵塞通信方法, 只运行一个 server, 系统负载就可以到达 左右. 因此我们推举使用 select.
参考文献:
UNIX Network Programming Volume 1 Stevens 1998 Prentice Hall
计算机有用网络编程汤毅坚 1993 人民邮电出版社
UNIX? SYSTEM V RELEASE 4 Programmer”s Guide:STREAMS AT&T 1990 Prentice Hall
UNIX? SYSTEM V RELEASE 4 Network Programmer”s Guide AT&T 1990 Prentice Hall
全部源程序均登载在 eDOC 网站上,如有需要可以去 :// 下载转载声明:本文转自 :// /Program/Control/