基于select模型的python echo server

# -*- coding: utf-8 -*-
# !/usr/bin/env python
###################################################
# Teaching Wisedom to My Computer,
# Please Call Me Croco,Fuck Your Foolishness.
##################################################

import sys
import socket
import select
import os
import traceback
import copy
import errno

import signal
import threading

isContinue_event = threading.Event()
is_sigint_up = False


def sigint_handler(signum, frame):
    global is_sigint_up
    global isContinue_event
    is_sigint_up = True
    isContinue_event.set()
    # print 'sigint_handler,catched interrupt signal!'


'''
class ServerInterface
 定义一个Server的通用接口,被InterruptableTaskLoop调用

'''


class ServerInterface(object):
    # ServerInterface.start()
    def start(self):
        raise NotImplementedError()

    # ServerInterface.stop()
    def stop(self):
        raise NotImplementedError()

    # ServerInterface.serve_once()
    def serve_once(self):
        raise NotImplementedError()

    # ServerInterface.notify()
    def notify(self, message):
        raise NotImplementedError()


#
# The `InterruptableTaskLoop` Class
#
class InterruptableTaskLoop(object):
    def __init__(self, worker, timeout=1):
        '''
        :param worker: requre methods is :start() serve_once() stop() notify()
        :param timeout:
        '''
        if not hasattr(worker, 'start'):
            raise AttributeError("AttributeError:miss method called start() ")
        if not hasattr(worker, 'serve_once'):
            raise AttributeError("AttributeError:miss method called serve_once() ")
        if not hasattr(worker, 'stop'):
            raise AttributeError("AttributeError:miss method called stop() ")
        if not hasattr(worker, 'notify'):
            raise AttributeError("AttributeError:miss method called notify() ")
        self.worker = worker
        self.timeout = timeout
        pass

    def wait(self):
        '''
        :return:
        '''
        try:
            isContinue_event.clear()
            isContinue_event.wait(timeout=self.timeout)
        except:
            pass

    def startAsForver(self):
        '''
        :return:
        '''
        global is_sigint_up
        global isContinue_event
        is_sigint_up = False
        signal.signal(signal.SIGINT, sigint_handler)
        isOK = self.worker.start()
        if not isOK:
            self.worker.notify("self.worker.start failed ,then system.exit")
            return

        while not is_sigint_up:
            try:
                self.wait()
                self.worker.serve_once()
            except Exception, e:
                message = "caught a exception in func>serve_once,message is:{0}".format(e.message)
                self.worker.notify(message)
        else:
            self.worker.notify("self.worker.stop,catched interrupt signal")
            self.worker.stop()


#################################################################
class EchoServer(ServerInterface):
    def __init__(self, listen_port):
        self._listen_port = listen_port
        self._listen_addr = ("0.0.0.0", self._listen_port)
        self._listen_backlog = 128
        self._time_out = 0.01
        pass

    # ServerInterface.start()
    def start(self):
        self._listen_socket_list = []
        self._acceptor_dict = {}
        self._session_id = 0
        self._is_started = 0
        try:
            listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            listen_socket.setblocking(False)
            listen_socket.bind(self._listen_addr)
            listen_socket.listen(self._listen_backlog)
            self._listen_socket_list.append(listen_socket)
            print "EchoServer::Start() listen in port:{0} pid:{1}".format(self._listen_addr,os.getpid())
            self._is_started = 1
            return True
        except:
            (ErrorType, ErrorValue, ErrorTB) = sys.exc_info()
            info = "listen failed:{0} {1} ".format(ErrorValue, self._listen_addr)
            print info
            self._is_started = 0
        return False

    # ServerInterface.stop()
    def stop(self):
        print "ServerInterface.stop()"
        for listen_socket in self._listen_socket_list:
            if listen_socket:
                listen_socket.close()
        self._listen_socket_list = []
        for acceptor in self._acceptor_dict.values():
            acceptor.onDisconnectEvent()
        self._acceptor_dict.clear()
        self._is_started = 0

    def _assignSessionId(self):
        self._session_id += 1
        return self._session_id

    def _addTcpAcceptor(self, acceptor):
        self._acceptor_dict[acceptor.client_socket] = acceptor
        print "_addTcpAcceptor sessionId:{0} fd:{1} addr:{2}".format(acceptor.client_session_id,
                                                                     acceptor.client_socket.fileno(),
                                                                     acceptor.client_addr)

    def _delTcpAcceptor(self, acceptor):
        if acceptor.client_socket in self._acceptor_dict:
            self._acceptor_dict[acceptor.client_socket]

            print "_delTcpAcceptor sessionId:{0} fd:{1} addr:{2}".format(acceptor.client_session_id,
                                                                         acceptor.client_socket.fileno(),
                                                                         acceptor.client_addr)
            del self._acceptor_dict[acceptor.client_socket]
            return
        print "_delTcpAcceptor nothing"


    def _onFdAccept(self, listen_socket):
        client_socket, client_address = listen_socket.accept()
        #client_socket.setblocking(False)
        acceptor = _Acceptor(self._assignSessionId(), client_socket, client_address, client_status=1)
        self._addTcpAcceptor(acceptor)

    def _onFdRead(self, client_socket):
        acceptor = self._acceptor_dict.get(client_socket, None)
        if acceptor is None:
            print "_onFdRead failed,"
            return
        acceptor.onReadEvent()

    def _onFdWrite(self, client_socket):
        acceptor = self._acceptor_dict.get(client_socket, None)
        if acceptor is None:
            print "_onFdWrite failed,"
            return
        acceptor.onWriteEvent()

    # 预备需要select的集合
    def _prepare_select_sets(self):
        inputs = copy.copy(self._listen_socket_list)
        outputs = []
        invalids = []
        for acceptor in self._acceptor_dict.values():
            if 1 != acceptor.client_status:
                invalids.append(acceptor)
                continue
            inputs.append(acceptor.client_socket)
            if acceptor.isNeedSend():
                outputs.append(acceptor.client_socket)
        for acceptor in invalids:
            self._delTcpAcceptor(acceptor)
            acceptor.onDisconnectEvent()
        return inputs, outputs

    # ServerInterface.serve_once()
    def serve_once(self):
        if 1 != self._is_started:
            return
        try:
            inputs, outputs = self._prepare_select_sets()
            readable, writable, exceptional = select.select(inputs, outputs, inputs, self._time_out)
            # When timeout reached , select return three empty lists
            if not (readable or writable or exceptional):
                # print "Time out ! "
                return
            self._process_exceptional(exceptional)
            self._process_writable_set(writable)
            self._process_readable_set(readable)

        except:
            info = sys.exc_info()
            for file, lineno, function, text in traceback.extract_tb(info[2]):
                str_info = "{0} line:{1} in function:{2}".format(file, lineno, function)
                print str_info
                str_text = "** %s: %s" % info[:2]
                print str_text
            sys.exit(102)

    def _process_readable_set(self, readable_set):
        for readable_socket in readable_set:
            if readable_socket in self._listen_socket_list:
                self._onFdAccept(readable_socket)
            else:
                self._onFdRead(readable_socket)

    def _process_writable_set(self, writable_set):
        for writable_socket in writable_set:
            self._onFdWrite(writable_socket)
        pass
        '''
        for sock 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)
        '''

    def _process_exceptional(self, exceptional_set):
        for exceptional_socket in exceptional_set:
            print "_process_exceptional:",exceptional_socket.fileno()
        pass


class _Acceptor(object):
    __slots__ = ['client_session_id', 'client_socket', 'client_addr', 'client_status', 'recv_packet_size',
                 'recv_buffer', 'send_buffer']

    def __init__(self, client_session_id=-1, client_socket=None, client_addr=(), client_status=0):
        self.client_session_id = client_session_id
        self.client_socket = client_socket
        self.client_addr = client_addr
        self.client_status = client_status
        self.recv_packet_size = 16 * 1024
        self.recv_buffer = ''
        self.send_buffer = ''

    def onReadEvent(self):
        while self.client_status == 1:
            try:
                data = self.client_socket.recv(self.recv_packet_size)
                if not data:
                    print "client_socket.recv 0 from addr:{0} sessionid:{1}".format(self.client_addr,
                                                                                    self.client_session_id)
                    self.client_status = -1
                    return
                self.recv_buffer += data
                processed_size = self._process_recv_buffer()
                if processed_size == len(self.recv_buffer):
                    self.recv_buffer = ''
                elif processed_size < len(self.recv_buffer):
                    self.recv_buffer = self.recv_buffer[processed_size:]
                elif processed_size == 0:
                    pass
                else:
                    print "client_socket._process_recv_buffer error from addr:{0} sessionid:{1}".format(
                        self.client_addr, self.client_session_id)
                    self.client_status = -1
            except socket.error, msg:
                if msg.errno == errno.EAGAIN or msg.errno == errno.EWOULDBLOCK:
                    #print "onReadEvent EAGAIN or EWOULDBLOCK"
                    pass
                else:
                    print "onReadEvent unhandle socket.error:{0}".format(msg)
                    self.client_status = -2
                return
            except Exception,e:
                print "onReadEvent unhandle Exception:{0}".format(e)
                self.client_status = -3
                return

        pass

    def onDisconnectEvent(self):
        if self.client_status == 0:
            return
        self.client_socket.close()
        self.client_socket = None
        self.client_status = 0
        self.send_buffer=''
        self.recv_buffer=''

    def onWriteEvent(self):
        if not self.isNeedSend():
            return
        ret = self.client_socket.send(self.send_buffer)
        print "write:", ret, len(self.send_buffer),self.send_buffer
        if ret == len(self.send_buffer):
            self.send_buffer=''
            return
        self.send_buffer = self.send_buffer[ret:]
        pass


    #是否有发送的必要
    def isNeedSend(self):
        if self.client_status == 0:
            return False
        if len(self.send_buffer) == 0:
            return False
        return True

    def sendData(self, data=""):
        if self.client_status == 0:
            return False
        self.send_buffer += data
        return True


    def _process_recv_buffer(self):
        pid=str(os.getpid())
        print '{0}-recv:'.format(pid), len(self.recv_buffer),self.recv_buffer
        self.sendData( pid+"say:"+self.recv_buffer)
        return len(self.recv_buffer)


if __name__ == '__main__':
    print __file__
    serv = EchoServer(1234)
    InterruptableTaskLoop(serv, 0.0).startAsForver()





客户端


# -*- coding: utf-8 -*-
# !/usr/bin/env python
###################################################
# Teaching Wisedom to My Computer,
# Please Call Me Croco,Fuck Your Foolishness.
##################################################

import sys
import socket
import select
import traceback
import errno

class ServerInterface(object):
    # ServerInterface.start()
    def start(self):
        raise NotImplementedError()

    # ServerInterface.stop()
    def stop(self):
        raise NotImplementedError()

    # ServerInterface.serve_once()
    def serve_once(self):
        raise NotImplementedError()

    # ServerInterface.notify()
    def notify(self, message):
        raise NotImplementedError()


#################################################################
class EchoClient(ServerInterface):
    def __init__(self, connect_addr,connection_pool_size=1,echo_text='hello'):
        self._connect_addr = connect_addr
        self._connection_pool_size = connection_pool_size
        self._time_out = 1
        self._echo_text=echo_text
        pass

    # ServerInterface.start()
    def start(self):
        self._connector_dict = {}
        self._is_started = 0
        try:
            for i in xrange(self._connection_pool_size):
                connect_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                connect_socket.connect(self._connect_addr)
                connect_socket.setblocking(False)
                self._connector_dict[connect_socket] = _Connector(client_socket=connect_socket,client_status=1)
                if i==0:
                     print "EchoClient::Start() connectOK to {0}".format(self._connect_addr)
            self._is_started = 1
            self._prepare_first_request()
            return True
        except:
            (ErrorType, ErrorValue, ErrorTB) = sys.exc_info()
            info = "connect failed:{0} {1} ".format(ErrorValue, self._connect_addr)
            print info
            self._is_started = 0
            sys.exit(100)
        return False

    # ServerInterface.stop()
    def stop(self):
        for connector in self._connector_dict.values():
            connector.onDisconnectEvent()
        self._connector_dict.clear()
        self._is_started = 0


    def _onFdRead(self, client_socket):
        connector = self._connector_dict.get(client_socket, None)
        if connector is None:
            print "_onFdRead failed,"
            return
        connector.onReadEvent()

    def _onFdWrite(self, client_socket):
        connector = self._connector_dict.get(client_socket, None)
        if connector is None:
            print "_onFdWrite failed,"
            return
        connector.onWriteEvent()

    # 预备需要select的集合
    def _prepare_select_sets(self):
        inputs = []
        outputs = []
        invalids = []
        for connector in self._connector_dict.values():
            #print "_prepare_select_sets fd:{0} status:{1}".format(connector.client_socket.fileno(),connector.client_status)
            if 1 != connector.client_status:
                invalids.append(connector)
                continue
            inputs.append(connector.client_socket)
            if connector.isNeedSend():
                outputs.append(connector.client_socket)
        for connector in invalids:
            if connector.client_socket in self._connector_dict:
                del self._connector_dict[connector.client_socket]
            connector.onDisconnectEvent()
        return inputs, outputs

    # ServerInterface.serve_once()
    def serve_once(self):
        # if 1 != self._is_started:
        #     print "service is not started ok,so exit"
        #     sys.exit(102)
        #     return
        try:
            inputs, outputs = self._prepare_select_sets()
            readable, writable, exceptional = select.select(inputs, outputs, inputs, self._time_out)
            # When timeout reached , select return three empty lists
            if not (readable or writable or exceptional):
                #print "serve_once() select Time out ! "
                return
            self._process_exceptional(exceptional)
            self._process_writable_set(writable)
            self._process_readable_set(readable)
        except:
            info = sys.exc_info()
            for file, lineno, function, text in traceback.extract_tb(info[2]):
                str_info = "{0} line:{1} in function:{2}".format(file, lineno, function)
                print str_info
                str_text = "** %s: %s" % info[:2]
                print str_text
            sys.exit(102)

    def _prepare_first_request(self):
        send_buffer=self._echo_text
        for connector in self._connector_dict.values():
            connector.echo_text = send_buffer
            connector.sendData(send_buffer)
            print "_prepare_first_request fd:{0} send_buffer:{1}".format(connector.client_socket.fileno(),len(send_buffer))
        pass


    def _process_readable_set(self, readable_set):
        if not  readable_set:
            return
        for readable_socket in readable_set:
            self._onFdRead(readable_socket)

    def _process_writable_set(self, writable_set):
        if not  writable_set:
            return
        for writable_socket in writable_set:
            self._onFdWrite(writable_socket)
        pass
        '''
        for sock 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)
        '''

    def _process_exceptional(self, exceptional_set):
        if not  exceptional_set:
            return
        for exceptional_socket in exceptional_set:
            print "_process_exceptional:",exceptional_socket.fileno()
        pass




class _Connector(object):
    __slots__ = ['client_socket', 'client_status', 'recv_packet_size','recv_buffer', 'send_buffer','echo_text']

    def __init__(self, client_socket=None,  client_status=0):
        self.client_socket = client_socket
        self.client_status = client_status
        self.recv_packet_size = 16 * 1024
        self.recv_buffer = ''
        self.send_buffer = ''
        self.echo_text="I am Connector"

    def onReadEvent(self):
        if self.client_status != 1:
            print "onReadEvent status error"
            return
        while self.client_status == 1:
            try:
                data = self.client_socket.recv(self.recv_packet_size)
                if not data:
                    print "client_socket.recv 0 from addr:{0} sessionid:{1}".format(self.client_addr,
                                                                                    self.client_session_id)
                    self.client_status = -1
                    return
                self.recv_buffer += data
                processed_size = self._process_recv_buffer()
                if processed_size == len(self.recv_buffer):
                    self.recv_buffer = ''
                elif processed_size < len(self.recv_buffer):
                    self.recv_buffer = self.recv_buffer[processed_size:]
                elif processed_size == 0:
                    pass
                else:
                    print "client_socket._process_recv_buffer error from addr:{0} sessionid:{1}".format(
                        self.client_addr, self.client_session_id)
                    self.client_status = -1
            except socket.error, msg:
                if msg.errno == errno.EAGAIN or msg.errno == errno.EWOULDBLOCK:
                    #print "onReadEvent EAGAIN or EWOULDBLOCK"
                    pass
                else:
                    print "onReadEvent unhandle socket.error:{0}".format(msg)
                    self.client_status = -2
                return
            except Exception,e:
                print "onReadEvent unhandle Exception:{0}".format(e)
                self.client_status = -3
                return

    def onDisconnectEvent(self):
        if self.client_status == 0:
            return
        print "onDisconnectEvent fd:{0}".format( self.client_socket.fileno() )
        self.client_socket.close()
        self.client_socket = None
        self.client_status = 0
        self.recv_buffer=''
        self.send_buffer=''


    def onWriteEvent(self):
        if not self.isNeedSend():
            return
        ret = self.client_socket.send(self.send_buffer)
        print "write:", ret, self.send_buffer
        if ret == len(self.send_buffer):
            #print "clear send buffer"
            self.send_buffer=''
            return
        self.send_buffer = self.send_buffer[ret:]
        pass

    def sendData(self, data=""):
        if self.client_status != 1:
            print "sendData Failed"
            return False
        self.send_buffer += data
        return True

    #是否有发送的必要
    def isNeedSend(self):
        if self.client_status == 0:
            return False
        if len(self.send_buffer) == 0:
            return False
        return True


    def _process_recv_buffer(self):
        print 'recv:', len(self.recv_buffer),self.recv_buffer
        self.sendData(self.echo_text)
        return len(self.recv_buffer)


import time
if __name__ == '__main__':
    print "BEGIN"
    cli = EchoClient( connect_addr=("127.0.0.1",1234),connection_pool_size=65,echo_text='1'*20)
    cli.start()
    while True:
        cli.serve_once()
        #time.sleep(0.1)

    print "END"





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值