python中使用select和poll的例子

本文介绍如何使用Python的select和poll模块创建一个回显服务器。该服务器接收客户端发送的数据,并将接收到的数据原样返回给客户端。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本例子实现一个回显服务器,当客户端发送一串字符到服务器后,服务器将该串字符返回到客户端。客户端的实现:

#!/usr/bin/env python
# encoding: utf-8


import socket
import sys

messages = [ 'This is the message. ']
server_address = ('localhost', 10000)


#创建一批套接字
socks = list(socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(100))



#每个套接字都连接到服务器
for s in socks:
    try:
        s.connect(server_address)
        print >>sys.stderr, 'connecting to %s port %s' % server_address
    except Exception as e:
        print e.message
#     errno = s.connect_ex(server_address)
#     print 'errno=%d'%errno

        
for message in messages:
    #往服务器发送消息
    for s in socks:
        print >>sys.stderr, '%s: sending "%s"' % \
            (s.getsockname(), message)
        s.send(message)
    #接受服务器返回的消息
    for s in socks:
        data = s.recv(1024)
        print >>sys.stderr, '%s: received "%s"' % \
            (s.getsockname(), data)
        if not data:
            print >>sys.stderr, 'closing socket', s.getsockname()
            s.close()

服务器的实现(采用select方式)

#!/usr/bin/env python
# encoding: utf-8

import select
import socket
import sys
import Queue


# 创建套接字并设置该套接字为非阻塞模式
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)

# 绑定套接字
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address)

# 将该socket变成服务模式
# backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
# 这个值不能无限大,因为要在内核中维护链接队列
server.listen(5)

#初始化读取数据的监听列表,最开始时我们希望从server这个套接字上读取数据
inputs = [ server ]

#初始化写入数据的监听列表,最开始时并没有客户端连接进来,所以列表为空
outputs = [ ]

#要发往客户端的数据
message_queues = {}

while inputs:
 
    print >>sys.stderr, 'waiting for the next event'
    #调用select监听所有监听列表中的套接字,并将准备好的套接字加入到对应的列表中
    readable, writable, exceptional = select.select(inputs,
                                                    outputs,
                                                    inputs)

     
    #处理可读的套接字
    """
        如果server这个套接字可读,则说明有新链接到来
        此时在server套接字上调用accept,生成一个与客户端通讯的套接字
        并将与客户端通讯的套接字加入inputs列表,下一次可以通过select检查该链接是否可读
        然后在发往客户端的缓冲中加入一项,键名为:与客户端通讯的套接字,键值为:空队列
        """
    """ 
        若可读的套接字不是server套接字,有两种情况:一种是有数据到来,另外一种是链接断开
        如果有数据到来,先接收数据,然后将收到的数据填入发往客户端的缓存区中的对应位置,最后
        将于客户端通讯的套接字加入到写数据的监听列表; 
        如果套接字可读,但是没有接收到数据,则说明客户端已经断开。这时要关闭与客户端连接的套接字,并进行资源清理
        """
     
    for s in readable:
        if s is server:
            connection, client_address = s.accept()
            print >>sys.stderr, '  connection from', client_address
            connection.setblocking(0)
            inputs.append(connection)
            message_queues[connection] = Queue.Queue()
            
        else:
            data = s.recv(1024)
            if data:
                print >>sys.stderr, '  received "%s" from %s' % \
                    (data, s.getpeername())
                message_queues[s].put(data)
                if s not in outputs:
                    outputs.append(s)
                     
            else:
                print >>sys.stderr, '  closing', client_address
                if s in outputs:
                    outputs.remove(s)
                inputs.remove(s)
                s.close()
                del message_queues[s]
 
    #处理可写的套接字
    """
       在发送缓冲区中取出响应的数据 ,发往客户端。
       如果没有数据需要写,则将套接字从发送对列中移除,select中不再监视 
    """
    for s in writable:
        try:
            next_msg = message_queues[s].get_nowait()
        except Queue.Empty:
            print >>sys.stderr, '  ', s.getpeername(), 'queue empty'
            outputs.remove(s)
        else:
            print >>sys.stderr, '  sending "%s" to %s' % \
                (next_msg, s.getpeername())
            s.send(next_msg)
 
    # 处理异常情况
    for s in exceptional:
        print >>sys.stderr, 'exception condition on', s.getpeername()
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()
        del message_queues[s]
 
#end while

服务器的实现(采用poll方式,只能在linux下运行)

#!/usr/bin/env python
# encoding: utf-8


import select
import socket
import sys
import Queue

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)

server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address)
server.listen(5)

message_queues = {}

TIMEOUT = 1000

"""
poll的底层实现比select高效,但是poll在windows下不被支持
"""

# poll监听5种事件
# POLLIN:输入准备好
# POLLPRI:带外数据可读
# POLLOUT:准备好接受数据
# POLLERR:有错误发生
# POLLHUP:通道关闭
# POLLVAL:通道未打开



#只读事件组合
READ_ONLY = ( select.POLLIN |
              select.POLLPRI |
              select.POLLHUP |
              select.POLLERR )
#读写事件组合
READ_WRITE = READ_ONLY | select.POLLOUT


#得到poll对象
poller = select.poll()
#在poll对象中注册server套接字,并让poller监听该套接字的READ_ONLY事件
poller.register(server, READ_ONLY)


#由于poller.poll返回的是元素为(fd,flag)的列表,所以我们必须保存fd与相应socket对象的映射
fd_to_socket = { server.fileno(): server,
               }

while True:
    #监听已注册的socket事件,返回元素为(描述符,事件标志)的列表
    print >>sys.stderr, 'waiting for the next event'
    events = poller.poll(TIMEOUT)

    for fd, flag in events:
        s = fd_to_socket[fd]
        
        #处理输入事件
        if flag & (select.POLLIN | select.POLLPRI):
            #当有新连接到来
            if s is server:
                connection, client_address = s.accept()
                print >>sys.stderr, '  connection', client_address
                connection.setblocking(0)
                fd_to_socket[ connection.fileno() ] = connection
                #将与客户端通讯的socket对象注册到poll对象中,并让poll监听该对象的只读属性
                poller.register(connection, READ_ONLY)
                message_queues[connection] = Queue.Queue()
                
            #客户端有数据到来
            else:
                data = s.recv(1024)
                if data:
                    print >>sys.stderr, '  received "%s" from %s' % \
                        (data, s.getpeername())
                    message_queues[s].put(data)
                    # 让poll对象监听该套接字对象的读写属性
                    poller.modify(s, READ_WRITE)
                #客户端断开
                else:
                    print >>sys.stderr, '  closing', client_address
                    poller.unregister(s)
                    s.close()
                    del message_queues[s]
                    

                        
        #套接字可写
        elif flag & select.POLLOUT:
            # Socket is ready to send data, if there is any to send.
            try:
                next_msg = message_queues[s].get_nowait()
            except Queue.Empty:
                # No messages waiting so stop checking
                print >>sys.stderr, s.getpeername(), 'queue empty'
                #如果没数据可写,则不监听写事件
                poller.modify(s, READ_ONLY)
            else:
                print >>sys.stderr, '  sending "%s" to %s' % \
                    (next_msg, s.getpeername())
                s.send(next_msg)
                
        #通道关闭           
        elif flag & select.POLLHUP:
            # Client hung up
            print >>sys.stderr, '  closing', client_address, '(HUP)'
            poller.unregister(s)
            s.close()
            del message_queues[s]
        
        #发生错误
        elif flag & select.POLLERR:
            print >>sys.stderr, '  exception on', s.getpeername()
            poller.unregister(s)
            s.close()
            del message_queues[s]


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值