接下来我会详细讲解Python实现socket非阻塞通讯的完整攻略。
什么是Socket非阻塞通讯
在网络编程中,我们常常需要使用Socket来进行网络通信。而在Socket的使用过程中,一般都会采用阻塞式编程方式。即当Socket收到请求或发送数据时,程序会一直等待,直到数据传输完成才会执行下一步操作。
而Socket非阻塞通讯则是指在Socket通信过程中,程序不会一直等待。当Socket收到请求或发送数据时,程序可以直接进行下一步操作,不必等待Socket传输完成。这种方式可以大大提高程序的并发性。
如何实现Socket非阻塞通讯
实现Socket非阻塞通讯的方式有多种。其中一种较为常见的方式是使用Python的select模块和非阻塞式Socket。
1. 创建非阻塞式Socket
我们可以使用Python的socket模块来创建Socket,并将其设置为非阻塞式:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
sock.bind(('127.0.0.1', 3000))
sock.listen(5)
在上面的代码中,我们使用了socket模块创建了一个TCP类型的Socket,并将其设置为非阻塞式。由于设置了非阻塞式,Socket在进行数据传输时就不会一直等待,而是会立即返回数据状态。同时,我们将Socket绑定到本地端口3000上,并开始监听客户端的连接请求。
2. 使用select模块进行数据交互
在Socket通信过程中,我们需要不断检查Socket是否有数据到来或者数据是否已经发送完成。这时我们可以使用Python的select模块。相较于阻塞式编程,这种方式会减少无效的等待时间,提升程序性能。
首先,我们需要使用select模块来创建I/O对象和I/O读写列表:
import select
inputs = [sock]
outputs = []
在上面的代码中,我们定义了一个reads列表和一个writes列表。分别用来存储需要读取和需要写入数据的Socket对象。
然后,我们可以使用select模块的select函数来进行I/O对象列表的监听:
while inputs:
rs, ws, xs = select.select(inputs, outputs, inputs)
for r in rs:
# 处理读取事件
pass
for w in ws:
# 处理写入事件
pass
for x in xs:
# 处理异常事件
pass
在上面的代码中,调用select.select()函数来监听输入对象列表,监听输出对象列表和监听异常条件,其中:
- rs: 表示可读I/O列表
- ws: 表示可写I/O列表
- xs: 表示异常I/O列表
在监听事件之后,我们需要根据当前的事件类型来进行读写操作。举个例子,如果rs列表中有可读数据,那么我们可以使用recv函数来读取数据:
for r in rs:
data = r.recv(1024)
if data:
# 处理数据
else:
# Socket连接已关闭
inputs.remove(r)
r.close()
在上面的代码中,我们使用recv()函数来读取数据,处理完数据后如果需要断开连接则调用close()函数关闭Socket。如果当前读取的Socket已经关闭,则从输入对象列表中删除它。
示例
接下来,我们可以看一下使用Python实现Socket非阻塞通讯的一个例子:
import select
import socket
# 创建非阻塞式Socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
sock.bind(('127.0.0.1', 3000))
sock.listen(5)
# 初始化select模块输入输出列表
inputs = [sock]
outputs = []
while inputs:
rs, ws, xs = select.select(inputs, outputs, [])
for r in rs:
if r is sock:
# 处理连接请求
conn, addr = sock.accept()
conn.setblocking(False)
inputs.append(conn)
else:
data = r.recv(1024)
if data:
# 处理数据
pass
else:
inputs.remove(r)
r.close()
for w in ws:
# 处理写入事件
pass
在上面的示例中,我们首先创建了一个非阻塞式Socket并将其绑定到端口3000上。然后我们创建了一个select模块的输入输出列表。
在程序运行时,我们首先使用select模块对输入输出列表进行监听。当rs列表中有可以读取的数据时,我们使用recv()函数读取数据并进行处理。当ws列表中有可以写入的数据时,我们使用send()函数写入数据。同时,当rs列表中有新的连接请求时,我们使用accept()函数接收连接,并将其添加到inputs列表中。
这个例子实现了一个简单的非阻塞式Socket服务器,并且可以同时接收多个客户端的连接和数据请求。
示例2
下面是另一个示例,可以让代码更加清晰明了:
import socket
import select
HOST = ''
PORT = 13333
MAX_LISTEN = 100
RECV_BUF_SIZE = 4096
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
def handle_client(connection):
data = connection.recv(RECV_BUF_SIZE)
print(data.decode('utf-8'))
connection.send(bytes('HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello', 'utf-8'))
connection.close()
def handle_server(server_socket):
server_socket.setblocking(0)
server_socket.listen(MAX_LISTEN)
inputs = [server_socket]
outputs = []
while True:
readable, writeable, exceptional = select.select(inputs, outputs, inputs)
for r in readable:
if r in inputs:
if r is server_socket:
connection, client_address = r.accept()
connection.setblocking(0)
inputs.append(connection)
else:
handle_client(r)
inputs.remove(r)
if r in outputs:
outputs.remove(r)
for w in writeable:
pass
for e in exceptional:
pass
if __name__ == '__main__':
try:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
print('HTTP Server listening on', PORT)
handle_server(server_socket)
except Exception as e:
print(e)
在这个例子中,我们首先定义了一个handle_client()函数来处理客户端请求。并定义了一个handle_server()函数来启动Non-Blocking Socket服务器。
然后,在handle_server函数中,我们设置了socket为非阻塞式,使用select模块来进行对象列表的监听。在监听到有readable对象时,我们进行数据读取和处理;当对象可以writeable时,我们进行数据写入处理。
在这个例子中,我们实现了一个简单的HTTP服务器,并且使其支持非阻塞式Socket通信。
【注】以上代码均未经过安全性和效率方面的审查和测试,仅作为示例。实际使用中需谨慎。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python实现socket非阻塞通讯功能示例 - Python技术站