【Pyhton Network】使用poll()或select()实现非阻塞传输
通常情况下,socket上的I/O会阻塞。即除非操作结束,否则程序不会照常进行。而以下集中情况需要在非阻塞模式下进行:
1. 网络接口在等待数据时是活动的,可以做出相应;
2. 在不使用线程或进程的情况下也可以同时处理多个网络相关任务;
3. 在网络上等待的时候可以执行其它计算
在以上情况中,可以使用两个标准工具解决,poll和select。它们都可以通知操作系统哪个socket对程序感兴趣,当该socket上有事件发生时,操作系统才调用处理程序。
服务器程序每隔5秒向连接对象发送系统当前时间,如下:
#! /usr/bin/env python # Delaying Server - Chapter 5 - delayserver.py import socket, traceback, time host = '' # Bind to all interface port = 51423 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) s.listen(1) while 1: try: clientsock, clientaddr = s.accept() except KeyboardInterrupt: raise except: traceback.print_exc() continue # Process the connection try: print "Got Connection from ", clientsock.getpeername() while 1: try: clientsock.sendall(time.asctime() + "\n") except: break time.sleep(5) except (KeyboardInterrupt, SystemExit): raise except: traceback.print_exc() # Close the connection try: clientsock.close() except KeyboardInterrupt: raise except: traceback.print_exc()
poll客户端程序如下:
#! /usr/bin/env python # Nonblocking I/O - Chapter 5- pollclient.py import socket, sys, select host = "localhost" port = 51423 spinsize = 10 spinpos = 0 spindir = 1 def spin(): global spinsize, spinpos, spindir spinstr = '.'*spinpos + '|' + '.'*(spinsize-spinpos-1) sys.stdout.write('\r'+spinstr+' ') sys.stdout.flush() spinpos += spindir if spinpos < 0: spindir = 1 spinpos = 1 elif spinpos >= spinsize: spinpos -= 2 spindir = -1 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.connect((host, port)) p = select.poll() p.register(s.fileno(), select.POLLIN | select.POLLERR | select.POLLHUP) while 1: results = p.poll(50) if len(results): if results[0][1] == select.POLLIN: data = s.recv(4096) if not len(data): print "\rRemote en closed connection; exiting." break # Only one item in here -- if there's anything, it's for me sys.stdout.write("\rReceived: "+data) sys.stdout.flush() else: print "\rProblem occured; exiting." sys.exit(0) spin()
等价的,select客户端如下:
#! /usr/bin/env python # Nonblocking I/O with select() - Chapter 5 - selectclient.py import socket, sys, select host = 'localhost' port = 51423 spinsize = 10 spinpos = 0 spindir = -1 def spin(): global spinsize, spinpos, spindir spinstr = '.'*spinpos + '|' + '.'*(spinsize-spinpos-1) sys.stdout.write('\r'+spinstr+' ') sys.stdout.flush() spinpos += spindir if spinpos < 0: spinpos = 1 spindir = 1 elif spinpos >= spinsize: spinpos -= 2 spindir = -1 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.connect((host, port)) while 1: infds, outfds, errfds = select.select([s], [], [s], 0.05) # [s] just the same as[s.fileno()], only need to be a list. if len(infds): # Normally, one would use something like "for fd in infds" here. # We don't bother since there will only ever be a single file # descriptor here. data = s.recv(4096) if not len(data): print "\rRemote and closed connection; exiting." break # Only one item in here -- if there's anything, it's for us. sys.stdout.write("\rReceived: "+data) sys.stdout.flush if len(errfds): print "\rProblem occurred; exiting." sys.exit(0) spin()
运行截图如下: