平时我们看到的非阻塞socket(select,poll,epoll)多用在服务器端,相比于客户端,我们很容易淹没在细节之中。下面是客户端使用select,poll的代码事例。
我们使用简单的同步socket获取http://192.168.9.178/cs.php?a=php的内容,
# -*- coding: utf-8 -*- import socket import select s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = '192.168.9.178' s.connect((host,80)) packet = """\ GET /cs.php?a=php HTTP/1.0 Host: %s """ % host contentLength=len(packet) #sync s.send(packet) print packet d=[] while 1: b=s.recv(100) if not b: break d.append(b) print ''.join(d) print 'done'
下面使用非阻塞socket发送同样请求,首先使用select,代码有意并没有一次发送或读取很多字节,这样通过打印我们就可以看到异步请求的过程。
# -*- coding: utf-8 -*- import socket import select s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = '192.168.9.178' s.connect((host,80)) packet = """\ GET /cs.php?a=php HTTP/1.0 Host: %s """ % host contentLength=len(packet) #async s.setblocking(0) start=0 d=[] while 1: r,w,e=select.select([s],[s],[s],10) if e: print 'errr' break if s in w: if start < contentLength: start += s.send(packet[start:start+20]) if s in r: b=s.recv(20) if not b: break d.append(b) print ''.join(d) print 'done'
select可直接处理类socket对象,只要具有fileno方法即可。
下面是用poll实现相同的功能,不管是select还是poll都要关注结束条件。由于poll比select多了for循环,所以通过标志位判断是否结束。
# -*- coding: utf-8 -*- import socket import select s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = '192.168.9.178' s.connect((host,80)) packet = """\ GET /cs.php?a=php HTTP/1.0 Host: %s """ % host contentLength=len(packet) #sync s.send(packet) print packet d=[] while 1: b=s.recv(100) if not b: break d.append(b) print ''.join(d) print 'done' #async poll s.setblocking(0) READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR READ_WRITE = READ_ONLY | select.POLLOUT poller=select.poll() poller.register(s,READ_WRITE) start=0 d=[] sfd = s.fileno() fail=full=0 while 1: events = poller.poll(1000) #单位毫秒 for fd,flag in events: #返回的是(fileno,falg)的列表 if fd is sfd: if flag & select.POLLOUT: if start < contentLength: #这边最好判断一下内容是否已发送完,因为发送完了fd还是可写的 start += s.send(packet[start:start+20]) if flag & (select.POLLIN | select.POLLPRI): #注意这边不要elif,因为fd经常是可读可写 b=s.recv(10) if not b: #有读事件,但为EOF,说明内容已结束 full=1 break d.append(b) if flag & select.POLLHUP: print 'POLLHUP' fail = 1 if full or fail: break print ''.join(d) print 'done'