python-socket编程(四)多线程

我们之前例子都是一个服务端同时只能接受一个客户端的请求,而我们实际的情况是,一个服务端可能要应对上百上千个客户端的请求。而这样的需求就只能通过多进程或者多线程的方法来进行实现,多线程和多进程具体是怎么实现,我们先不做深入探究,先看一个简单的多线程的实例。

实现代码

  • 服务端
import socketserver

class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print('conn is:',self.request)
        print('addr is:',self.client_address)

        while True:
            #收消息
            data=self.request.recv(1024)
            print('收到的客户端消息是',data)

            #发消息
            self.request.sendall((data.upper()))


if __name__ == '__main__':
    s = socketserver.ThreadingTCPServer(('127.0.0.1',8001),MyServer)
    s.serve_forever()

客户端的代码使用上两节最普通的基础代码即可,现在我们详解一下这个服务端的代码。

代码实现逻辑

socketserver模块中所定义的几个类的继承关系。

  • ThreadingTCPServer --> TCPServer --> BaseServer
  • ThreadingTCPServer --> ThreadingMixIn

然后我们就来看看上面的代码,每一步是如何实现的

  • 1、产生实例
s = socketserver.ThreadingTCPServer(('127.0.0.1',8001),MyServer)

这一步是生成了一个实例s,这个实例是由ThreadingTCPServer这个类产生的,查询此模块的源代码。

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

可见此类源代码中没有任何特殊属于自己的方法以及属性,全部继承于ThreadingMixIn和TCPServer,那我们继续查看TCPServer的源代码。

  • 2、TCPServer类的初始化
class TCPServer(BaseServer):
	#套接字的地址家族
	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):
		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

从上述代码中发现初始化传入的参数是self,server_address,RequestHandlerClass,那对应代码中的就是实例s,(‘127.0.0.1’,8001)以及MyServer这个我们自己定义的请求接收类。

TcpServer类会调用父类BaseServer的init方法,主要是将两个参数的值保存在实例属性中。

class BaseServer:
    def __init__(self, server_address, RequestHandlerClass):
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False
  • 3、地址绑定
    接下来,实例会产生一个套接字对象,同时将地址和端口进行绑定并进行监听。其中用到两个方法server_bind()和server_activate(),本质上就是socket模块的bind和listen的封装。这两个方法都属于TcpServer
    def server_bind(self):
        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):
        self.socket.listen(self.request_queue_size)

至此初始化的步骤就已经结束,套接字已经开始监听。

  • 5、执行s.serve_forever()方法
    这个方法是属于BaseServer类下面的方法
def serve_forever(self, poll_interval=0.5):
    self.__is_shut_down.clear()
    try:
    	with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

其中由很多涉及线程的知识,我们着重_handle_request_noblock()这个方法入手

    def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request() #conn,clientaddress
        except OSError:
            return
        if self.verify_request(request, client_address):
        #判断request, client_address是否为空
            try:
                self.process_request(request, client_address)
            except:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
        else:
            self.shutdown_request(request)

其中get_get_request()这个方法是TCPServer类中的一个方法,会返回self.socket.accept()这个方法的结果,而这个套接字方法我们知道会返回两个结果,一个conn对象和客户端地址。

因此request就是conn对象,client_address就是客户端地址。

判断两者若不为空,则会执行process_request方法

    def process_request(self, request, client_address):
        self.finish_request(request, client_address)
        self.shutdown_request(request)
    def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)

而这个RequestHandlerClass就是我们自己写的代码中的Myserver类(类实例初始化的时候忘记了self.RequestHandlerClass = RequestHandlerClass有这么一行代码吗),所以s.serve_forever最后相当于是在运行s.Myserver类的代码

  • 6、运行Myserver类代码
class MyServer(socketserver.BaseRequestHandler)

Myserver类继承于BaseRequestHandler父类

class 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()

在对这个类进行初始化的时候,就会运行handle方法。而我们自定义的handle方法就会开始运转,进入收发消息的循环中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值