python3socket非阻塞_Python实现socket的非阻塞式编程

阻塞模式与非阻塞模式

阻塞模式 程序碰到了一些耗时操作,无法继续向下走。

例如在socket编程中,例如在send()即发送信息过程中,可能对方已经断开,可能网络等原因导致信息传递不通畅;在客户端的connect()函数中,可能地址不可达等原因。这些情况在阻塞模式中会造成线程中断等待,导致无法进行下一步操作,等超过一个固定时间还没有完成之后会产生异常。但是这种阻塞通常用于确定的几个连接地址并且必须准确连接上。

非阻塞模式 当程序碰到耗时操作,分发给别的线程,主线程继续执行。

例如在socket编程中,在send()或connect()函数中,程序会抛出异常10035,在非阻塞模式下无法完成耗时操作,但是程序会继续走下去,不会阻塞到当前的程序。那么,怎么判断什么时候程序完成这些耗时操作呢?select闪亮登场。

基于select的网络编程

在python中,select函数是一个队底层操作系统直接访问的接口,它用来监控sockets、files和pipse,等待IO完成。当有可读、可写或者是异常事件产生时,select函数可以监控到。

r, w, e, = select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。这个就是系统级别的阻塞,如果监控到事件会直接传递到这里。

具体的客户端非阻塞连接如下,使用的是python3.6

# -*- coding: UTF-8 -*-

# python使用select进行非阻塞模式编程,客户端程序

import socket

import select

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 生成socket

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 不经过WAIT_TIME,直接关闭

sock.setblocking(False) # 设置非阻塞编程

try:

# sock.connect(("google.com", 80))

sock.connect(("192.168.1.106", 789))

except Exception as e:

print(e)

r_inputs = set()

r_inputs.add(sock)

w_inputs = set()

w_inputs.add(sock)

e_inputs = set()

e_inputs.add(sock)

while True:

try:

r_list, w_list, e_list = select.select(r_inputs, w_inputs, e_inputs, 1)

print("r") # 产生了可读事件,即服务端发送信息

for event in r_list:

try:

data = event.recv(1024)

except Exception as e:

print(e)

if data:

print(data)

print("收到信息")

else:

print("远程断开连接")

r_inputs.clear()

print("w")

if len(w_list) > 0: # 产生了可写的事件,即连接完成

print(w_list)

w_inputs.clear() # 当连接完成之后,清除掉完成连接的socket

print("e")

if len(e_list) > 0: # 产生了错误的事件,即连接错误

print(e_list)

e_inputs.clear() # 当连接有错误发生时,清除掉发生错误的socket

except OSError as e:

print(e)

非阻塞服务端代码如下,同样使用python3.6

# -*- coding: UTF-8 -*-

import socket

import select

sock = socket.socket()

sock.bind(('192.168.1.106', 789))

sock.setblocking(False)

sock.listen()

inputs = [sock, ]

while True:

r_list, w_list, e_list = select.select(inputs, [], [], 1)

for event in r_list:

if event == sock:

print("新的客户端连接")

new_sock, addr = event.accept()

inputs.append(new_sock)

else:

data = event.recv(1024)

if data:

print("接收到客户端信息")

print(data)

event.send(b'\x31')

else:

print("客户端断开连接")

inputs.remove(event)

python的select注意事项

除了select,还有什么提高效率的神器

通过上述文章可知效率对比 epoll > poll > select

但是在windows系统上只有select,所以常常用以下判断

if hasattr(select, 'epoll'):

self._impl = select.epoll()

model = 'epoll'

elif hasattr(select, 'kqueue'):

self._impl = KqueueLoop()

model = 'kqueue'

elif hasattr(select, 'select'):

self._impl = SelectLoop()

model = 'select'

切记如果在非阻塞情况下缠上了10035的错误,那是正常反应,我们只需用select进行获取即可。(我不会告诉你我被这个坑折腾了好几天)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值