SocketServer模块源码分析

python 提供的SocketServer 模块是一个多线程或多进程(既可以用多进程,也可以用多线程,取决于用户自己的需求)的Socket模块,能帮助我们更快的开发一个SocketServer服务器,我们在使用SocketServer模块开发时不用关心内部的实现方法,python已经帮我们封装好了。我们只需要关心客户端连接后的业务实现即可。

用户端SocketServer实现例子:

import socketserver
import os

class Myserver(socketserver.BaseRequestHandler):

    def handle(self):
        client_obj = self.request
        client_ip = self.client_address
        print("client {0}:{1} connected!".format(client_ip[0],client_ip[1]))
        # 发送给客户端连接成功信息
        msg = "connections successfull"
        print(type(msg))
        msg1 = msg.encode('utf-8')
        print(type(msg1))
        client_obj.sendall(msg1)

        while True:
            # 接收客户端输入的命令
            recv_cmd = client_obj.recv(1024).decode('utf-8')
            print("cmd:{0},{1} ".format(recv_cmd,type(recv_cmd)))

            # 获取系统命令结果
            cmd_res = os.popen(recv_cmd).read().strip()
            # 将结果转换为bytes格式
            cmd_res = cmd_res.encode('utf-8')
            print(cmd_res.decode('utf-8'))
            # 获取转换后的内容长度
            cmd_res_len = len(cmd_res)

            # 先把结果长度发给客户端
            print(str(cmd_res_len))
            client_obj.send(str(cmd_res_len).encode('utf-8'))

            # 接收客户端响应
            recv_status = client_obj.recv(1024)
            print("[%s] recv result len status:%s" %(client_ip,recv_status))

            # 再发结果内容
            client_obj.sendall(cmd_res)

if __name__ == "__main__":
    print("Server Started!waitting for client..")
    server = socketserver.ThreadingTCPServer(("127.0.0.1",8090),Myserver)
    server.serve_forever()

如上所示,用户只需要定义一个自己的Myserver类,写一个handle()方法即可。
那么SocketServer内部的实现是怎样的呢?
看下图:
Socket内部调用逻辑图

下面就来对它进行剖析

1 初始化
如上图所示,当用户执行socketserver.ThreadingTCPServer()实例化对象时,首先会去找__init__构造函数
以下为构造函数查找过程:
首先,去socketserver.ThreadingTCPServer()函数中找,结果发现啥都没有

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

由于 ThreadingTCPServer类继承至 ThreadingMixIn 和 TCPServer , 按照优先级从左至右的原则,先去 ThreadingMixIn中找。
ThreadingMixIn 类
看到没,就2个方法。还是没有。
那就再去 TCPServer 类中找,看到没,果真有耶!!
TCPServer构造函数
但是有没有发现这个类继承至BaseServer类,调用了该类的构造函数,那就去看看里面有什么
基类构造函数
看到了吧,这个里面其实很简单,就是把我们的定义的地址及端口数据封装到了类变量中,这里就等价于

self.server_address = ("127.0.0.1",8090)
self.RequestHandlerClass = Myserver
...

再回到TCPServer类的__init__构造函数,执行的主要函数如下:

# 实例化一个socket
self.socket = socket.socket(self.address_family,self.socket_type)
# 绑定并监听
self.server_bind() ==执行了==> self.socket.bind(self.server_address)
self.server_activate() == 执行了 ==> self.socket.listen(self.request_queue_size)

到此,初始化就完成了。
总结:
初始化的时候分别通过TCPServer、BaseServer的构造函数初始化,初始化的实质就是实例化了一个socket对象,并开启监听。

2 运行服务
下面看看运行时执行的代码干了那些事情

server.server_forever()

打开server_forver方法,发现其实干了2件事:

1 用 select 监听客户端的连接

r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval)

这里的_eintr_retry其实就是执行select方法,具体函数是:

def _eintr_retry(func, *args):
    """restart a system call interrupted by EINTR"""
    while True:
        try:
            return func(*args)
        except OSError as e:
            if e.errno != errno.EINTR:
                raise

翻译过来等价于 select.select([self], [], [],poll_interval)

2 建立连接

 if self in r:
     self._handle_request_noblock()

就是当有连接过来时(服务端发生变化)后,调用_handle_request_noblock()方法

def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request()
        except OSError:
            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)

这里面主要的执行是:
1) request, client_address = self.get_request() # 接受客户端请求,建立连接

2)调用 self.process_request(request, client_address) 方法
你会发现在BaseServer 和 ThreadingMixIn 类均有这个方法,根据类的继承特性,会调用 ThreadingMixIn类的 process_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()

很明显,当有客户端连接之后,模块会新建立一个线程 来处理客户端的请求,处理由 process_request_thread 方法完成,此方法是存在 基类 BaseServer中

    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)

在这个里面调用了 RequestHandlerClass()方法。
这个RequestHandlerClass 是我们刚初始化时传入的 自定义类,

3 执行业务逻辑处理
上面我们已经分析到当客户端建立连接请求后,模块会新建一个线程来调用我们定义的类,执行处理。 那么我们自定义的类是怎么工作的呢,为什么我们自定义类必须要定义
一个Hand方法。
我们看看自定义类:

class Myserver(socketserver.BaseRequestHandler):

    def handle(self):
      ...

自定义类继承了 socketserver.BaseRequestHandler 类,那我们就看看这个里面干了些什么?

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

很明显,这个类初始化完后就是将客户端对象、客户端地址、服务对象传递给了对象, 执行了一个handle方法
所以我们必须要定义个handle方法。也就是处理我们所有业务逻辑的地方

总结:
当有客户端请求时,干了以下几件事:
1 select 监听服务端状态
2 有请求时新起一个线程处理请求
3 处理请求的方法调用我们自己定义的handle方法处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值