【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()
View Code

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()
View Code

等价的,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()
        
        
View Code

运行截图如下:

posted on 2014-03-11 10:21  Bombe  阅读(328)  评论(0编辑  收藏  举报

导航