zoukankan      html  css  js  c++  java
  • SocketServer模块

    基本概念

        SocketServer内部使用 IO多路复用以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

    原理

    1、服务器启动socket监听端口
    2、服务器内部利用while循环监视句柄的变化
    3、客户端请求
    4、服务器为这个请求分配线程或进程(底层调用select)。

    SocketServer模块有两个方法ThreadingTCPServerForkingTCPServer,分别创建线程或者进程

    ThreadingTCPServer

    定义
        ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
    使用方法:
        1、创建一个继承自 SocketServer.BaseRequestHandler 的类
        2、类中必须定义一个名称为 handle 的方法(名称不能更改)
        3、启动ThreadingTCPServer
    server代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import SocketServer
    class MyServer(SocketServer.BaseRequestHandler):
        def handle(self):       #函数名必须是handle,源码BaseRequestHandler预定义;
            print self.request,self.client_address,self.server  #BaseRequestHandler的__init__定义好的;
            conn = self.request     #每个连接的socket
            conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
            Flag = True
            while Flag:
                data = conn.recv(1024)
                if data == 'exit':
                    Flag = False
                elif data == '0':
                    conn.sendall('通话可能会被录音.balabala一大推')
                else:
                    conn.sendall('请重新输入.')
    if __name__ == '__main__':
        server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)   #实例化ThreadingTCPServer,其实是继承TCPServer类的初始化,需要传入(server_address, RequestHandlerClass),RequestHandlerClass就是MyServer类。
        server.serve_forever()  #启动服务,用select一直在循环执行,继承自BaseServer类的方法serve_forever();
    client代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import socket
    ip_port = ('127.0.0.1',8009)
    sk = socket.socket()
    sk.connect(ip_port)
    sk.settimeout(5)
    while True:
        data = sk.recv(1024)
        print 'receive:',data
        inp = raw_input('please input:')
        sk.sendall(inp)
        if inp == 'exit':
            break
    sk.close()

    ForkingTCPServer

    ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “”,该线程用来和客户端进行交互。
    ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程”  和 “进程”。
    server代码不同处:
    1. server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyRequestHandler)
    2. 变更为:
    3. server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)

    ThreadingTCPServer源码分析(ForkingTCPServer方法类似

    类继承关系图


    服务器启动程序后:
    1、执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
        解释class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass,ThreadingTCPServer先要执行__init__构造函数,ThreadingMixIn里没有,就去TCPServer类里去找
    2、执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
        解释:TCPServer构造方法中包含BaseServer.__init__(self, server_address, RequestHandlerClass),所以要把自己定义的类传给BaseServer的构造方法
    3、执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
        解释:serve_forever是BaseServer类中的方法,里面有个while循环,一直调用select.select(),r, w, e = _eintr_retry(select.select, [self], [], [],poll_interval)
    客户端接入:
    4、执行BaseServer._handle_request_noblock方法
        解释:serve_forever的while循环里有一个判断if self in r:self._handle_request_noblock(),客户端连接句柄发生变化就会把句柄放到r列表里,所以,触发了_handle_request_noblock()。
    5、执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
        解释:调用process_request方法时,从继承类广度优先原则,所以它先调用ThreadingMixIn类中的process_request
    6、执行 ThreadingMixIn.process_request_thread 方法
        解释:t = threading.Thread(target = self.process_request_thread,args = (request, client_address))多线程模块方法,调用self.process_request_thread,此时才真正启动了线程。
    7、执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用自定义的MyRequestHandler的handle方法)
        解释:连接创建完成,此时开始执行handle方法中的内容,开始和客户端交互,执行完,后面再执行shutdown_request方法关闭连接。

    源码精简

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import socket
    import threading
    import select
    def process(request, client_address):
        print request,client_address
        conn = request
        conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
        flag = True
        while flag:
            data = conn.recv(1024)
            if data == 'exit':
                flag = False
            elif data == '0':
                conn.sendall('通过可能会被录音.balabala一大推')
            else:
                conn.sendall('请重新输入.')
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sk.bind(('127.0.0.1',8002))
    sk.listen(5)
    while True:
        r, w, e = select.select([sk,],[],[],1)
        print 'looping'
        if sk in r:
            print 'get request'
            request, client_address = sk.accept()
            t = threading.Thread(target=process, args=(request, client_address))
            t.daemon = False
            t.start()
    sk.close()
        如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。























  • 相关阅读:
    002-数据库命名开发规范
    006-多线程-基础-同步解决 概述【ReentrantLock、Semaphore、CyclicBarrier、CountDownLatch】
    005-多线程-基础-sleep、join、yield、wait、notify、notifyAll、run、start、synchronized
    004-多线程-基础-同步问题引出、同步问题解决、死锁、生产者与消费者
    003-多线程-基础-其他【命名和取得、休眠、优先级、线程状态、线程中断】
    002-多线程-基础-实现方式【thread、runnable、callale、thread和runnable对比】
    001-多线程-基础-进程线程、线程状态、优先级、用户线程和守护线程
    001-Spring在代码中获取bean的几种方式
    004-mysql explain详解
    java-序列化-001-原生介绍
  • 原文地址:https://www.cnblogs.com/daliangtou/p/5084439.html
Copyright © 2011-2022 走看看