文成小盆友python-num10 socketserver 原理相关。
本节主要内容:
1.IO多路复用
2.多线程多进程
3.小知识点补充(python中作用域相关)
4.socketserver源码分析补充
一。IO多路复用
I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
linux中的io多路复用机制:select,poll,epoll
select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。 poll poll在1986年诞生于System V Release ,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。 epoll 直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
python中的select模块:
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。
但是再不同的平台,它们所提供的内容支持是不相同的如下:(网络操作,文件操作,终端操作均都属于IO操作,对于windows只支持Socket操作,其他系统支持其他IO操作,但是无法检测 普通文件操作 自动上次读取是否已经变化。)
Windows Python:
提供: select
Mac Python:
提供: select
Linux Python:
提供: select、poll、epoll
select方法如下:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间) 参数: 可接受四个参数(前三个必须)
返回值:三个列表 select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
简单的io多路复用实例:
####简单的io多路复用(伪并发) -- 12345
import socket
import select sk = socket.socket()
sk.bind(('127.0.0.1',9999,))
sk.listen(5)
inputs = [sk,] while True:
rlist,w,e, = select.select(inputs,[],[],1)
print(len(inputs),len(rlist))
print(type(rlist),rlist) for r in rlist:
if r == sk:
print(r)
conn,address = r.accept()
inputs.append(conn)
conn.sendall(bytes("hello",encoding='utf-8')) else:
r.recv(1024)
简单的IO多路复用,实现读写分离:
####简单的io多路复用(实现读写分离) -- 123456
import socket
import select sk = socket.socket()
sk.bind(('127.0.0.1',9999,))
sk.listen(5)
inputs = [sk,]
outputs = [] while True:
rlist,wlist,e, = select.select(inputs,outputs,[],1)
print(len(inputs),len(rlist),len(outputs),len(wlist)) for r in rlist:
if r == sk:
print(r)
conn,address = r.accept()
inputs.append(conn)
conn.sendall(bytes("hello",encoding='utf-8')) else:
#开始接收消息了
print("recv".center(60,"*"))
try:
ret = r.recv(1024)
if not ret:
raise Exception("断开链接")
else:
outputs.append(r)
print(outputs) except Exception as e:
inputs.remove(r)
for w in wlist:
w.sendall(bytes("----response----",encoding="utf-8"))
outputs.remove(w)
二.多线程多进程
线程:多任务可以由多进程完成,也可以由一个进程内的多线程完成,一个进程内的所有线程,共享同一块内存python中创建线程比较简单,导入threading模块如下所示:
def f1(i):
time.sleep(1)
print(i) if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=f1, args=(i,))
t.start()
print('start') # 主线程等待子线程完成,子线程并发执行 >>start
>>2
>>1
>>3
>>0
>>4
主线程从上到下执行,创建5个子线程,打印出'start',然后等待子线程执行完结束,如果想让线程要一个个依次执行完,而不是并发操作,那么就要使用join方法:
import threading
import time def f1(i):
time.sleep(1)
print(i) if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=f1, args=(i,))
t.start()
t.join()
print('start') # 线程从上到下依次执行,最后打印出start >>0
>>1
>>2
>>3
>>4
>>start
上面的代码不使用join的结果就是主线程会默认等待子线程结束,才会结束,如果不想让主线程等待子线程的话,可以子线程启动之前设置将其设置为后台线程,如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止,前台线程则相反,若果不加指定的话,默认为前台线程,下面从代码来看一下,如何设置为后台线程。例如下面的例子,主线程直接打印start,执行完后就结束,而不会去等待子线程,子线程中的数据也就不会打印出来
import threading
import time def f1(i):
time.sleep(1)
print(i) if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=f1, args=(i,))
t.setDaemon(True)
t.start() print('start') # 主线程不等待子线程 >> start
除此之外,自己可以为线程自定义名字,通过 t = threading.Thread(target=f1, args=(i,), name='mythread{}'.format(i)) 中的name参数,除此之外,Thread还有一下一些方法
- t.getName() : 获取线程的名称
- t.setName() : 设置线程的名称
- t.name : 获取或设置线程的名称
- t.is_alive() : 判断线程是否为激活状态
- t.isAlive() :判断线程是否为激活状态
- t.isDaemon() : 判断是否为守护线程
进程:
线程的上一级就是进程,进程可包含很多线程,进程和线程的区别是进程间的数据不共享,多进程也可以用来处理多任务,不过多进程很消耗资源,计算型的任务最好交给多进程来处理,IO密集型最好交给多线程来处理,此外进程的数量应该和cpu的核心说保持一致。
(进程内容下篇会再次补充--)
三。小知识点补充(python中作用域相关)
python 中的作用域相关问题补充:
- python 中没有块级作用域
- python中是以函数为作用域
- python 中 作用域 链 从内往外找。
- python 的作用域在执行之前已经确定。
#python 中无块级作用域名 if 1 == 1:
name = "zhaowencheng"
print(name) ##
for i in range(10):
name = i
print(name) ##打印内容如下:
zhaowencheng
9 Process finished with exit code 0
def fuck():
name = "zhaowencheng" print(name) #-----报错内容为未定义。 #显示如下
NameError: name 'name' is not defined Process finished with exit code 1
name = "zhaowencheng" def f1():
print(name) def f2():
name = "zhao"
f1() f2() #显示如下:
zhaowencheng Process finished with exit code 0
li = [x+100 for x in range(10)]
print(li) li = [lambda :x for x in range(10)] r = li[0]()
print(r) ##显示如下:
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
9 Process finished with exit code 0
四。socket server源码分析补充:
再次重申下使用ThreadingTCPServer的要素:
- 创建一个继承自 SocketServer.BaseRequestHandler 的类
- 类中必须定义一个名称为 handle 的方法
- 启动ThreadingTCPServer
ThreadingTCPserver.BaseRequestHandler的源码实现,主要函数关系以及调用顺序:

内部调用流程如下:(主要流程)
- 启动服务端程序
- 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
- 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
- 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
- 当客户端连接到达服务器
- 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
- 执行 ThreadingMixIn.process_request_thread 方法
- 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
主要源码粘贴如下:
class BaseServer:
    """Base class for server classes.
    Methods for the caller:
    - __init__(server_address, RequestHandlerClass)
    - serve_forever(poll_interval=0.5)
    - shutdown()
    - handle_request()  # if you do not use serve_forever()
    - fileno() -> int   # for select()
    Methods that may be overridden:
    - server_bind()
    - server_activate()
    - get_request() -> request, client_address
    - handle_timeout()
    - verify_request(request, client_address)
    - server_close()
    - process_request(request, client_address)
    - shutdown_request(request)
    - close_request(request)
    - handle_error()
    Methods for derived classes:
    - finish_request(request, client_address)
    Class variables that may be overridden by derived classes or
    instances:
    - timeout
    - address_family
    - socket_type
    - allow_reuse_address
    Instance variables:
    - RequestHandlerClass
    - socket
    """
    timeout = None
    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False
    def server_activate(self):
        """Called by constructor to activate the server.
        May be overridden.
        """
        pass
    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.
        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            while not self.__shutdown_request:
                # XXX: Consider using another file descriptor or
                # connecting to the socket to wake this up instead of
                # polling. Polling reduces our responsiveness to a
                # shutdown request and wastes cpu at all other times.
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
    def shutdown(self):
        """Stops the serve_forever loop.
        Blocks until the loop has finished. This must be called while
        serve_forever() is running in another thread, or it will
        deadlock.
        """
        self.__shutdown_request = True
        self.__is_shut_down.wait()
    # The distinction between handling, getting, processing and
    # finishing a request is fairly arbitrary.  Remember:
    #
    # - handle_request() is the top-level call.  It calls
    #   select, get_request(), verify_request() and process_request()
    # - get_request() is different for stream or datagram sockets
    # - process_request() is the place that may fork a new process
    #   or create a new thread to finish the request
    # - finish_request() instantiates the request handler class;
    #   this constructor will handle the request all by itself
    def handle_request(self):
        """Handle one request, possibly blocking.
        Respects self.timeout.
        """
        # Support people who used socket.settimeout() to escape
        # handle_request before self.timeout was available.
        timeout = self.socket.gettimeout()
        if timeout is None:
            timeout = self.timeout
        elif self.timeout is not None:
            timeout = min(timeout, self.timeout)
        fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
        if not fd_sets[0]:
            self.handle_timeout()
            return
        self._handle_request_noblock()
    def _handle_request_noblock(self):
        """Handle one request, without blocking.
        I assume that select.select has returned that the socket is
        readable before this function was called, so there should be
        no risk of blocking in get_request().
        """
        try:
            request, client_address = self.get_request()
        except socket.error:
            return
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
    def handle_timeout(self):
        """Called if no new request arrives within self.timeout.
        Overridden by ForkingMixIn.
        """
        pass
    def verify_request(self, request, client_address):
        """Verify the request.  May be overridden.
        Return True if we should proceed with this request.
        """
        return True
    def process_request(self, request, client_address):
        """Call finish_request.
        Overridden by ForkingMixIn and ThreadingMixIn.
        """
        self.finish_request(request, client_address)
        self.shutdown_request(request)
    def server_close(self):
        """Called to clean-up the server.
        May be overridden.
        """
        pass
    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)
    def shutdown_request(self, request):
        """Called to shutdown and close an individual request."""
        self.close_request(request)
    def close_request(self, request):
        """Called to clean up an individual request."""
        pass
    def handle_error(self, request, client_address):
        """Handle an error gracefully.  May be overridden.
        The default is to print a traceback and continue.
        """
        print '-'*40
        print 'Exception happened during processing of request from',
        print client_address
        import traceback
        traceback.print_exc() # XXX But this goes to stderr!
        print '-'*40
BaseServer
BaseServer
class TCPServer(BaseServer):
    """Base class for various socket-based server classes.
    Defaults to synchronous IP stream (i.e., TCP).
    Methods for the caller:
    - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
    - serve_forever(poll_interval=0.5)
    - shutdown()
    - handle_request()  # if you don't use serve_forever()
    - fileno() -> int   # for select()
    Methods that may be overridden:
    - server_bind()
    - server_activate()
    - get_request() -> request, client_address
    - handle_timeout()
    - verify_request(request, client_address)
    - process_request(request, client_address)
    - shutdown_request(request)
    - close_request(request)
    - handle_error()
    Methods for derived classes:
    - finish_request(request, client_address)
    Class variables that may be overridden by derived classes or
    instances:
    - timeout
    - address_family
    - socket_type
    - request_queue_size (only for stream sockets)
    - allow_reuse_address
    Instance variables:
    - server_address
    - RequestHandlerClass
    - socket
    """
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 5
    allow_reuse_address = False
    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise
    def server_bind(self):
        """Called by constructor to bind the socket.
        May be overridden.
        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()
    def server_activate(self):
        """Called by constructor to activate the server.
        May be overridden.
        """
        self.socket.listen(self.request_queue_size)
    def server_close(self):
        """Called to clean-up the server.
        May be overridden.
        """
        self.socket.close()
    def fileno(self):
        """Return socket file number.
        Interface required by select().
        """
        return self.socket.fileno()
    def get_request(self):
        """Get the request and client address from the socket.
        May be overridden.
        """
        return self.socket.accept()
    def shutdown_request(self, request):
        """Called to shutdown and close an individual request."""
        try:
            #explicitly shutdown.  socket.close() merely releases
            #the socket and waits for GC to perform the actual close.
            request.shutdown(socket.SHUT_WR)
        except socket.error:
            pass #some platforms may raise ENOTCONN here
        self.close_request(request)
    def close_request(self, request):
        """Called to clean up an individual request."""
        request.close()
TCPServer
TCPServer
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the
# main process
daemon_threads = False def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread. In addition, exception handling is done here. """
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request) def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start() ThreadingMixIn
ThreadingMixIn
class BaseRequestHandler:
    """Base class for request handler classes.
    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.
    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define arbitrary other instance variariables.
    """
    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()
    def setup(self):
        pass
    def handle(self):
        pass
    def finish(self):
        pass
SocketServer.BaseRequestHandler
BaseRequestHandler
++++++++++++
文成小盆友python-num10 socketserver 原理相关。的更多相关文章
- 文成小盆友python-num7 -常用模块补充 ,python 牛逼的面相对象
		本篇内容: 常用模块的补充 python面相对象 一.常用模块补充 1.configparser模块 configparser 用于处理特定格式的文件,起内部是调用open()来实现的,他的使用场景是 ... 
- 文成小盆友python-num11-(2) python操作Memcache Redis
		本部分主要内容: python操作memcache python操作redis 一.python 操作 memcache memcache是一套分布式的高速缓存系统,由LiveJournal的Brad ... 
- 文成小盆友python-num12  Redis发布与订阅补充,python操作rabbitMQ
		本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ... 
- 文成小盆友python-num9 socket编程
		socket编程 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思 ... 
- 文成小盆友python-num5 -装饰器回顾,模块,字符串格式化
		一.装饰器回顾与补充 单层装饰器: 如上篇文章所讲单层装饰器指一个函数用一个装饰器来装饰,即在函数执行前或者执行后用于添加相应的操作(如判断某个条件是否满足). 具体请见如下: 单层装饰器 双层装饰器 ... 
- 文成小盆友python-num4 装饰器,内置函数
		一 .python 内置函数补充 chr() -- 返回所给参数对应的 ASCII 对应的字符,与ord()相反 # -*- coding:utf-8 -*- # Author:wencheng.z ... 
- 文成小盆友python-num3 集合,函数,-- 部分内置函数
		本接主要内容: set -- 集合数据类型 函数 自定义函数 部分内置函数 一.set 集合数据类型 set集合,是一个无序且不重复的元素集合 集合基本特性 无序 不重复 创建集合 #!/bin/en ... 
- 文成小盆友python-num2 数据类型、列表、字典
		一.先聊下python的运行过程 计算机是不能够识别高级语言的,所以当我们运行一个高级语言程序的时候,就需要一个“翻译机”来从事把高级语言转变成计算机能读懂的机器语言的过程.这个过程分成两类,第一种是 ... 
- 文成小盆友python-num15 - JavaScript基础
		一.JavaScript简介 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的 ... 
随机推荐
- 最新Android 出现Please ensure that adb is correctly located at问题的解决方法
			最近经常遇到下面的问题 遇到问题描述: 运行android程序控制台输出: [2013-07-23 17:28:06 - ] The connection to adb is down, and a ... 
- Android 开源框架ActionBarSherlock初探
			1:我的开发环境是Ubuntu12.04下:adt-bundle-linux-x86-20130729. ActionBarSherlock下载地址:http://actionbarsherlock. ... 
- Entity Framework with MySQL 学习笔记一(验证标签)
			直接上代码 [Table("single_table")] public class SingleTable { [Key] public Int32 id { get; set; ... 
- HttpClient 发送 HTTP、HTTPS 请求的简单封装
			import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.Http ... 
- 【HDOJ】1494 跑跑卡丁车
			DP,将能量映射为0~14,注意当选择这圈加速的时候,这圈就不能再储存能量,同时能量14可能转化为10. #include <cstdio> #include <cstring> ... 
- Spark standalone安装(最小化集群部署)
			Spark standalone安装-最小化集群部署(Spark官方建议使用Standalone模式) 集群规划: 主机 IP ... 
- Android笔记(三):View一些值得注意的地方
			Button android:textAllCaps="false" // Button上的英文字符不转成大写 EditText android:maxLines="2& ... 
- POJ 2697 A Board Game (bfs模拟)
			比较水的一道题,在4*4的棋盘上有黑白子,现在有某种移动方式,问能否通过它将棋盘从某个状态移动到另一种状态 只要想好怎么保存hash表来去重,其他就差不多了... #include <iostr ... 
- WebMagic的设计参考了业界最优秀的爬虫Scrapy
			http://webmagic.io/docs/zh/posts/ch1-overview/thinking.html https://github.com/psvehla/liferay-sprin ... 
- JavaScript中的面向对象的讨论(转)
			前言 今天,WEB2.0时代的到来,给了JavaScript又一次大展身手的机会.Web2.0借助JavaScript技术,使得客户端的Web体验更加丰富多彩,同时JavaScript面对的问题域也变 ... 
