socketserver模块

摘要:
SocketServer模块简化了编写网络服务程序的任务。同时,SocketServer模块也是Python标准库中许多服务器框架的基础。套接字服务器模块可以简化网络服务器的编写。Python将网络服务抽象为两个主要类。一个是Server类,它用于处理与连接相关的网络操作,另一个是RequestHandler类,它用来处理与数据相关的操作。

  虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Python标准库中很多服务器框架的基础

  socketserver模块可以简化网络服务器的编写,Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。

一、单线程示例

  server端和客户端持续聊天的示例,但是同时只能和一个客户端进行通信,并没有并发的效果。

1、服务端代码

from socket import *
ip_port=('127.0.0.1',8880)

tcp_socket_server=socket()
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

while 1:

    conn,addr=tcp_socket_server.accept()
    print('客户端',addr)
    while 1:
        client_data=conn.recv(1024)
        if len(client_data)==0:
              print("客户端断开连接,等待新的用户连接....")
              break
        print ("接受数据 >>>",str(client_data,"utf8"))
        response=input("响应数据 >>>")
        conn.sendall(bytes(response,"utf8"))

    conn.close()

2、客户端代码

import socket
ip_port = ('127.0.0.1',8888)
sock = socket.socket()
sock.connect(ip_port)
print ("客户端启动:")

while True:

    inp = input('发送数据 >>>')
    if inp == 'exit':
        break
    sock.sendall(bytes(inp,"utf8"))
    server_response=sock.recv(1024)
    print ("服务端响应数据 >>>",str(server_response,"utf8"))

sock.close()

二、socketserver的使用模式

import socketserver

class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
    def handle(self):
        """
        并发的业务逻辑
        conn(客户端套接字对象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if client_data.decode("utf8") == "exit":
                  print("客户端断开连接,等待新的用户连接....")
                  break
            print("接受数据 >>>",str(client_data, "utf8"))
            response=input("响应数据 >>>")
            self.request.sendall(bytes(response, "utf8"))

        self.request.close()

# 封装TCP协议相关的套接字对象
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一个参数是ip+port,第二个参数是类名MyServer

server.serve_forever()

1、总结socketserver使用分以下三步:

(1)自定义功能类

class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
    def handle(self):
        """
        并发的业务逻辑
        conn(客户端套接字对象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if len(client_data)==0:
                  print("客户端断开连接,等待新的用户连接....")
                  break
            print ("接受数据 >>>",str(client_data,"utf8"))
            response=input("响应数据 >>>")
            self.request.sendall(bytes(response,"utf8"))

        self.request.close()

(2)封装TCP协议相关的套接字对象

server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一个参数是ip+port,第二个参数是类名MyServer

(3)调用server.serve_forever()

server.serve_forever()

2、运行客户端服务端显示效果

  socketserver模块第1张   socketserver模块第2张   socketserver模块第3张

三、socketserver源码解析

1、socketserver.ThreadingTCPServer源码解析

(1)查看ThreadingTCPServer类

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

  在socketserver.py中查看到这句源码,混合继承了ThreadingMixIn和TCPServer类。根据广度优先先查看左边的类。

(2)查看ThreadingMixIn类

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):...

    def process_request(self, request, client_address):...

  发现这个类并没有__init__方法,只有两个实例方法。因此再回去查看TCPServer类。

(3)查看TCPServer类中的__init__方法

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):
        """
        构造函数
        :param server_address: 接收的要绑定的元组('127.0.0.1', 8880)
        :param RequestHandlerClass: 接收功能类MyServer
        :param bind_and_activate:
        """
        BaseServer.__init__(self, server_address, RequestHandlerClass)   # 执行BaseServer的__init__方法
        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
    """后面代码省略"""

  注意__init__方法接收的参数,server_address: 接收的要绑定的元组('127.0.0.1', 8880);RequestHandlerClass: 接收功能类MyServer。

  __init__方法第一步就是执行BaseServer的__init__方法。

(4)查看BaseServer的__init__方法

class BaseServer:
    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

    """代码省略"""

  可以看到主要是做了一个实例变量赋值

(5)继续解析TCPServer类中的__init__方法

  在执行完BaseServer的__init__方法后,接下里开始创建socket对象:

self.socket = socket.socket(self.address_family,
                            self.socket_type)

  socket对象创建完成后,执行socket.bind和socket.listen方法:

class TCPServer(BaseServer):
    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        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)   # bind元组地址
        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)   # listen,这里默认值也是5

(6)总结server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer) 做的事情

  1.self.socket  2.self.socket.bind()  3.self.socket.listen(5) 

  这三步完成后,初始化也就完成了。

2、server.serve_forever()源码解析

  由于server是ThreadingTCPServer的实例对象,因此依次追溯ThreadingTCPServer、ThreadingMixIn、TCPServer、BaseServer类,最终在BaseServer类中找到serve_forever方法。

(1)查看serve_forever方法

class BaseServer:
    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()
        try:
            with _ServerSelector() as selector:   # IO多路复用监听
                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()

  前面两步都是监听相关的操作,直接查看self._handle_request_noblock()源码。

(2)查看_handle_request_noblock方法

  同样是依次追溯各个类,最后在BaseServer类中找到方法:

class BaseServer:
    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 Exception:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
            except:
                self.shutdown_request(request)
                raise
        else:
            self.shutdown_request(request)

(3)查看self.get_request()方法

class TCPServer(BaseServer):

    def get_request(self):
        """Get the request and client address from the socket.
        May be overridden.
        """
        return self.socket.accept()

  在这里找到了socket.accept方法,被动接受TCP客户的连接,(阻塞式)等待连接到来。

  因此_handle_request_noblock中的request, client_address = self.get_request():request就是新的套接字对象(conn),可以用来接收和发送数据;client_address就是连接客户端的地址

  继续查看_handle_request_noblock方法的self.process_request(request, client_address)调用。

(4)查看process_request方法

  这里按照查找顺序,在ThreadingMixIn类找到process_request方法。(注意不要找BaseServer里的process_request,它已经被覆盖了)

class ThreadingMixIn:

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

  每来一个用户就开启一个线程,执行self.process_request_thread(request, client_address)方法。

  process_request_thread方法也在ThreadingMixIn类中。

(5)查看process_request_thread方法

class ThreadingMixIn:
    def process_request_thread(self, request, client_address):
        try:
            self.finish_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
        finally:
            self.shutdown_request(request)

  按照查找顺序在BaseServer中找到finish_request方法。

(6)查看finish_request方法

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

  注意这里的RequestHandlerClass在TCPServer类中的__init__方法执行时,用RequestHandlerClass来接收功能类MyServer。

  这里是在调用MyServer类的实例化。

class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
    def handle(self):
        """
        并发的业务逻辑
        conn(客户端套接字对象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if client_data.decode("utf8") == "exit":
                  print("客户端断开连接,等待新的用户连接....")
                  break
            print("接受数据 >>>",str(client_data, "utf8"))
            response=input("响应数据 >>>")
            self.request.sendall(bytes(response, "utf8"))

        self.request.close()

  但是这个类并没有__init__方法,因此需要查看它的父类__init__方法。

(7)查看BaseRequestHandler类

class BaseRequestHandler:

    def __init__(self, request, client_address, server):
        self.request = request     # 新的套接字对象(conn)实例变量赋值
        self.client_address = client_address    # 连接客户端的地址实例变量赋值
        self.server = server
        self.setup()
        try:
            self.handle()  # 调用自己的handle方法
        finally:
            self.finish()

  在完成新的套接字对象(conn)实例变量赋值和连接客户端的地址实例变量赋值后,调用自己的handle方法。

  这个self.handle()调用的不是BaseRequestHandler类的handle方法而是MyServer的handle方法,执行自定义的handle方法

3、流程图

  socketserver模块第4张

免责声明:文章转载自《socketserver模块》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇性能测试篇:LoadRunner11 压力测试实例笔记C++中使用Json的方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

网络请求的超时原因

网络的抽象表示 操作系统中,所有网络请求的建立和数据传输,在软件层面是使用socket(一般翻译为套接字)来表示,可以把它理解为搭建在网络请求的客户端和服务端之间的一个管道,socket的建立类比管道的搭建,数据的传输类比管道内水的流动。套接字是基于TCP/IP实现的,它是TCP的接口在编程语言内的抽象,比如在Java语言中就有对应的ServerSock...

IO多路复用

IO模型介绍                                           为了更好地了解IO模型,我们需要事先回顾下:同步、异步、阻塞、非阻塞     同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问...

【转】nginx 和 php-fpm 通信使用unix socket还是TCP,及其配置

原文: http://blog.csdn.net/pcyph/article/details/46513521 -------------------------------------------------------------------------------------------------------------------- 前言 ngi...

C# Socket编程 同步以及异步通信

套接字简介:套接字最早是Unix的,window是借鉴过来的。TCP/IP协议族提供三种套接字:流式、数据报式、原始套接字。其中原始套接字允许对底层协议直接访问,一般用于检验新协议或者新设备问题,很少使用。 套接字编程原理:延续文件作用思想,打开-读写-关闭的模式。 C/S编程模式如下: Ø服务器端: 打开通信通道,告诉本地机器,愿意在该通道上接受客户请求...

zookeeper适用场景:配置文件同步

问题导读:1.本文三个角色之间是什么关系?2.三个角色的作用是什么?3.如何代码实现这三个角色的作用?在 zookeeper适用场景:zookeeper解决了哪些问题有关于分布式集群配置文件同步问题的描述,本文介绍如何把zk应用到配置文件分发的场景。假设有三个角色 trigger:发布最新的配置文件数据,发送指令和数据给zk_agent,实现是下面的tr...

C#高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)

网址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879 引言 我一直在探寻一个高性能的Socket客户端代码。以前,我使用Socket类写了一些基于传统异步编程模型的代码(BeginSend、BeginReceive,等等)也看过很多博客的知识,在linux中有poll和epoll来实现,在...