40.网络编程之SocketServer模块

 

 

SocketServer模块

 

SocketServer 是标准库中一个高级别的模块。用于简化网络客户与服务器的实现。模块中,已经实现了一些可供使用的类

Socketserver模块简化了编写网络服务程序,同时socketserver模块也是python标准库中很多服务器框架的基础。

 

SocketServer模块的类

 

BaseServer: 包含服务器的核心功能与混合(mix-in)类的钩子功能。这个类用于派生,不要直接生成这个类的类对象,
        可以考虑使用TCPServer 和 UDPServer。 TCPServer/UDPServer: 基本的网络同步 TCP/UDP 服务器 UnixStreamServer/UnixDatagramServer: 基本的基于文件同步 TCP/UDP 服务器 ForkingMixIn/ThreadingMixIn: 实现了核心的进程化或线程化的功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。
                不要直接生成这个类的对象 ForkingTCPServer/ForkingUDPServer: ForkingMixIn 和 TCPServer/UDPServer 的组合 ThreadingTCPServer/ThreadingUDPServer: ThreadingMixIn 和 TCPServer/UDPServer 的组合 BaseRequestHandler: 包含处理服务请求的核心功能。只用于派生新的类,不要直接生成这个类的对象,             可以考虑使用 StreamRequestHandler 或 DatagramRequestHandler StreamRequestHandler/DatagramRequestHandler: TCP/UDP 服务器的请求处理类的一个实现

 

 

 

socketserver模块类介绍

 

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

 

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

 

Server类

它包含了五种server类,分别是  Baseserver(不直接对外服务);TCPServer(使用TCP协议) ;UDPServer(使用UDP协议),UinixStreamServer    和UnixDatagramServer(后面两个仅仅在unix环境下有用,一般不常用)

他们五个的关系如下:

 

 

RequestHandler类

所有requestHandler都继承BaseRequestHandler基类。

请求的基类是baserequesthandler,其中一般需要重写的方法就是handle方法,主要就是如何处理接下里的请求,在这个类中,主要有三个方法,分别是setup ,handle ,finish方法,在调用这个类的时候,先调用setup进行一些初始化的工作,然后调用handle方法进行处理请求,然后调用finish方法,做一些关闭连接什么的,在这个里面最主要的参数就是self.request,也就是请求的socket对象,其中可以发送消息sendall或者send,接受消息就是recv

在请求处理的子类中有两个,一个是SreamRequestHandle和DatagramRequestHandle,在这个里面重写了基类的setup方法和finish方法,handle方法没有重写,因为这个是留给用户做处理请求的方法

 

混合类(并发类)

两个混合类,一个是ForkingMixin,主要是用fork的,产生一个新的进程去处理;一个是ThreadingMixin,产生一个新的线程,主要是用来提供异步处理的能力,其余tcpserver和udpserver组合,又产生了新的四个类,从而提供异步处理的能力。

 (在使用混合类和服务器类的时候,注意混合类需要写在前面,因为混合类重写了服务器类的方法,从而需要放在第一个位置。)

 

class socketserver.ForkingTCPServer
 
class socketserver.ForkingUDPServer
 
class socketserver.ThreadingTCPServer
 
class socketserver.ThreadingUDPServer

 

 

 

ThreadingTCPServer

ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

 

ThreadingTCPServer基础

1、使用ThreadingTCPServer:

1.1创建一个继承自 SocketServer.BaseRequestHandler 的类

1.2类中必须定义一个名称为 handle 的方法

1.3 启动ThreadingTCPServer

 

 

 SocketServer实现服务器

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer

class MyServer(SocketServer.BaseRequestHandler):

    def handle(self):
        # print self.request,self.client_address,self.server
        conn = self.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('请重新输入.')


if __name__ == '__main__':
    server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
    server.serve_forever()

 

客户端:

#!/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()

 

ThreadingTCPServer源码剖析

 

ThreadingTCPServer的类图关系如下:

 

 

内部调用流程为:

1、启动服务端程序

2、执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口

3、执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass

4、执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...

5、当客户端连接到达服务器

6、执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求

7、执行 ThreadingMixIn.process_request_thread 方法

8、执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)

 

 

ThreadingTCPServer相关源码:

BaseServer

 

通过:在Pycharm执行

import SocketServer
SocketServer.BaseServer  #在BaseServer点击CTRL+鼠标左键即可看到BaseServer的源码

 

 

TCPServer

import SocketServer
SocketServer.TCPServer  #TCPServer点击CTRL+鼠标左键即可看到TCPServer的源码

同理,ThreadingMixIn、 ThreadingTCPServer

 

 

RequestHandler相关源码

 

import SocketServer
SocketServer.BaseRequestHandler  #BaseRequestHandler点击CTRL+鼠标左键即可看到BaseRequestHandler的源码

 

 

 

实例:

服务器:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer

class MyServer(SocketServer.BaseRequestHandler):

    def handle(self):
        # print self.request,self.client_address,self.server
        conn = self.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('请重新输入.')


if __name__ == '__main__':
    server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
    server.serve_forever()

 

 

客户端:

#!/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()

 

 

 

源码精简:

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之所以可以同时处理请求得益于 selectThreading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)

 

 

ForkingTCPServer

ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程”  和 “进程”。

 

服务器

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer

class MyServer(SocketServer.BaseRequestHandler):

    def handle(self):
        # print self.request,self.client_address,self.server
        conn = self.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('请重新输入.')


if __name__ == '__main__':
    server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyServer)
    server.serve_forever()

 

客户端

#!/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 实例中的代码:

server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyRequestHandler)
变更为:
server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)

 

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 os.fork 两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,当前新创建的进程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

源码剖析参考 ThreadingTCPServer

posted @ 2019-06-19 09:18  钟桂耀  阅读(336)  评论(0编辑  收藏  举报