非阻塞模式开发

接字的非阻塞模式是指套接字在执行操作时,调用的函数不管操作是否完成都会立即返回的工作模式。非阻塞套接字在处理同时建立的多个连接,发送和接收的数据量不均,时间不定等方面具有明显的优势。但这种套接字在使用上存在一定难度。本章讲述套接字的非阻塞模式及其一个远程算数运算套接字程序。

套接字的非阻塞模式

所有windows平台都支持套接字以阻塞模式和非阻塞模式的方式工作。

非阻塞模式

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

设置套接字的非阻塞模式

当使用socket(),WSASocket()创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket(),将该套接字设置为非阻塞模式。

套接字设置为非阻塞模式后,在调用WindowsSocketsAPI函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。

WSAEWOULDBLOCK的含义

函数名

说明

accept(),WSAAcept()

应用程序没有接收到连接请求

recv(),WSARecv(),recvfrom(),WSARecvfrom()

接收缓冲区没有收到数据

send(),WSASend(),sendto(),WSASendto()

发送缓冲区此时不可用

connect(),WSAConnect()

连接未能立即完成

closesocket()

通常情况下意味着应用程序使用SO_LINGER选项并且设置一个非零的超时值,调用了setsocketopt()

需要说明的是并非所有的WindowsSocketsAPI在非阻塞模式下调用,都会返回WSEWOULDBLOCK错误。eg:bind(),listen()

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

非阻塞模式套接字的优势和不足 

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

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

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

但是,非阻塞套接字在控制建立的多个连接,在数据的收发量不均,时间不定等方面,明显具有优势。通常情况下,可考虑使用套接字的“I/O模型”,它有助于应用程序通过异步方法,同时对一个或多个套接字的通信加以管理。

阻塞模式是指当函数执行条件不满足时,线程会暂停执行直至条件满足或超时;非阻塞模式则是无论条件是否满足,函数立即返回,通过错误码反馈状态 [^1]。 两者的区别体现在多个方面。性能上,阻塞模式在处理少量任务时效率较高,但面对高并发时容易造成资源浪费;非阻塞模式可以更高效地利用 CPU 资源,能有效处理多个并发连接,在高并发环境下保持良好性能 [^4][^5]。编程复杂度方面,阻塞模式编程简单直接,易于理解和维护;非阻塞模式需要更多的控制逻辑和事件驱动模型,编程逻辑相对复杂,开发人员需要处理操作立即返回带来的各种情况,如错误处理、轮询机制或事件驱动机制的实现等 [^4][^5]。 在应用场景上,阻塞模式适合于单线程、低并发的场景,例如在处理单个客户端请求时,使用阻塞模式可简化代码逻辑 [^2][^4]。非阻塞模式更适合于高并发、实时性要求较高的场景,如在高并发的服务器端,可以使用非阻塞模式处理客户端连接;在服务器程序中,单个线程可通过轮询或事件驱动的方式,同时处理多个客户端的连接请求、数据接收和发送等操作 [^2][^5]。 ```python import socket # 阻塞模式示例 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 8888)) server_socket.listen(1) print('Waiting for connection...') conn, addr = server_socket.accept() # 阻塞直到有客户端连接 print(f'Connected by {addr}') data = conn.recv(1024) # 阻塞直到接收到数据 print(f'Received: {data.decode()}') conn.close() # 非阻塞模式示例 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.setblocking(0) # 设置为非阻塞模式 try: client_socket.connect(('localhost', 8888)) except BlockingIOError: pass import select ready = select.select([client_socket], [], [], 5) if ready[0]: client_socket.sendall(b'Hello, server!') ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值