天天看点

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

http://blog.csdn.net/hongqun/article/details/19161841

#!/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]