一、socketserver简介

  socketserver是一个创建服务器的框架,封装了许多功能用来处理来自客户端的请求,简化了自己写服务端代码。比如说对于基本的套接字服务器(socket-based servers),里面就定义了地址族(AF_INET,AF_UNIX等)、套接字类型(SOCK_STREAM,SOCK_DGRAM)等,此外对于基于请求的服务器(request-based servers),里面就详细叙述了如何处理客户端认证,多重请求,以及如何实现多线程多进程等。 

二、server种类

  在serketserver里有5种类型的server,他们的继承关系如下图,下面的4个类都是同步处理请求的,不支持异步处理,同步处理意思是下一个请求必须等待上一个请求被处理完才能开始。如果要让他们支持异步处理,需要继承ForkingMixIn or ThreadingMixIn,使他们支持继承多进程和多线程。比如说定义一个多线程UDPserver需要这样写 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

  

  如果要自己写一个server类,首先要继承基本请求处理类baseRequestHandler,然后重写他的 handle()方法来实现你自己的请求处理,需要注意的是要区分开StreamRequestHandler or DatagramRequestHandler(TCp or UDP)

1.BaseServer

  BaseServer是其他几种server类的基类,类中定义的方法有下面这些:

Methods for the caller:

    - __init__(server_address, RequestHandlerClass)         #初始化 可扩长
- serve_forever(poll_interval=0.5)                #被构造函数调用激活服务器
- shutdown()                             #关闭server_forever循环
- handle_request() # if you do not use serve_forever()   #请求处理函数,可能会被阻塞
                                      #get_request->verify_request->process_request
- fileno() -> int # for selector   #返回一个整型文件操作符供服务器监听, Methods that may be overridden: - server_bind()                           #绑定到要监听的地址上
- server_activate()                         #被构造函数调用激活服务器,
- get_request() -> request, client_address           #获取请求
- handle_timeout()                         #处理超时
- verify_request(request, client_address)            #验证请求
- server_close()                          #关闭服务器
- process_request(request, client_address)           #进程处理,调用finish_request 完成请求
- shutdown_request(request)                    #被shutdown调用,关闭一个单独的请求
- close_request(request)                      #关闭请求
- service_actions()                         #被serve_forever 调用
- handle_error()                           #处理错误 

2、TCPServer

  TCPServer是一种基本的套接字服务器类,采用TCP协议,继承自BaseServer,这里我会着重讲下TCPServer,然后自己写一个TCPserver,首先我们来看下在Socketserver源码中看下代码实现,

class TCPServer(BaseServer):

    address_family = socket.AF_INET                        # 定义地址族,ipv4
socket_type = socket.SOCK_STREAM                  # 定义套接字类型,流式套接字(TCP)
  request_queue_size = 5                        #定义监听的数量
allow_reuse_address = False                     # 定义是否要设置套接口的选项 def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
BaseServer.__init__(self, server_address, RequestHandlerClass)       #调用BaseServer的初始化
self.socket = socket.socket(self.address_family,self.socket_type)      #创建一个套接字
if bind_and_activate:
try:
self.server_bind()
self.server_activate()                    #调用bind()和activate()
except:  
self.server_close()                            #关闭server
raise def server_bind(self):                                 #绑定监听地址 if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)   #设定socket
self.socket.bind(self.server_address)                       #绑定地址
self.server_address = self.socket.getsockname()             #获取待连接客户端的地址 def server_activate(self):                             
self.socket.listen(self.request_queue_size)                   #监听  def server_close(self):
self.socket.close()                                 #关闭 def fileno(self):
return self.socket.fileno()                            #文件描述符 def get_request(self):
return self.socket.accept()                            #接受客户端的连接 def shutdown_request(self, request):
try:
request.shutdown(socket.SHUT_WR)                       #关闭之前先调用shutdown保证通信双方数据不丢失
except OSError:
pass                                        #之后再调用close
self.close_request(request) def close_request(self, request):                           #释放套接字的描述符,直接调用可能丢弃传输队列中的数据
request.close()

3、BaseRequestHandler和StreamRequestHandler  

  如果自己要实现TCPServer,我们还要自己定义一个请求处理类,而且要继承BaseRequestHandler或者StreamRequestHandler,并且覆盖他的handle()处理函数,用来处理请求。下面看一下他们的定义:

class BaseRequestHandler
def __init__(self, request, client_address, server):       #初始化request,client_addres server
self.request = request
self.client_address = client_address
self.server = server
self.setup()                             #在handle()方法前调用,初始化所有需要的
try:
self.handle()
finally:
self.finish() def setup(self):
pass def handle(self):                            #具体的处理请求函数,需要被覆盖
pass def finish(self):                            #在handle()方法好后调用,清理工作
pass class StreamRequestHandler(BaseRequestHandler): rbufsize = -1        #为流式套接字定义rfile和wfile
wbufsize = 0 timeout = None disable_nagle_algorithm = False            #当wbufsize !=0才会用到,避免太小的包 def setup(self):
self.connection = self.request
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize) #一个类似文件的对象,可以用来接收客户端的数据
self.wfile = self.connection.makefile('wb', self.wbufsize)   #一个类似文件的对象,可以向客户端返回数据 def finish(self):
if not self.wfile.closed:                     #wfile关闭之前需要刷新
try:
self.wfile.flush()
except socket.error:
pass
self.wfile.close()
self.rfile.close()

  到这里我们就可以自己编写一个单线程server类来,按照自己的要求来处理请求了,下面写了一个最简单的客户端与服务端通信的例子,主要是为了方便大家好理解。代码实例如下,打开两个命令行工具,首先运行server.py,在另外一个命令行运行client.py,会看到服务端收到的数据为'hello,server',客户端收到的数据为'你好'。

#server.py
import socketserver
class myTCPHandle(socketserver.StreamRequestHandler):         
def handle(self):                             #定义自己的请求处理函数 
print("connet from %s:%s" % self.client_address)         #打印客户端的地址
self.data=self.rfile.readline()                   #读取客户端的传过来的一行数据
print(str(self.data,encoding="utf-8"))
self.wfile.write(bytes('你好',encoding='utf-8'))         #发送数据‘你好’给客户端
if __name__=='__main__':
address=('127.0.0.1',9999)
server=socketserver.TCPServer(address,myTCPHandle)          #创建一个基于TCPServer的套接字
server.serve_forever()                          #调用它的server_forever()函数处理一个请求直到它被shutdown #client.py
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)         #创建套接字
client.connect(('127.0.0.1',9999))                     #要连接的地址和端口 data='hello,server\n'                             #发送的数据 \n作为行结束
data_bytes=bytes(data,encoding="utf-8")                  #转换成字节发送
client.send(data_bytes)                            #发送数据
data_recive=client.recv(1024)                        #接受数据
print(str(data_recive,encoding='utf-8'))                  #将接受的数据转换成字符串
client.close()                                 #关闭套接字

4、ThreadingMixIn

如果要实现以支持多线程的server,就需要继承ThreadingMixIn类,下面是源码

class ThreadingMixIn:

    daemon_threads = False

    def process_request_thread(self, request, client_address):         #处理请求的线程函数

        try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request) def process_request(self, request, client_address):
                                            #生成一个线程
t = threading.Thread(target = self.process_request_thread,args = (request, client_address))
t.daemon = self.daemon_threads
t.start()                                  #启动线程

在这里对于ForkingMixIn(多线程)、UDPServer、DatagramRequestHandler(数据报套接字)等我就不再一一介绍,他们的原理和多进程以及上面介绍的TCPServer基本一样。最后我会用一个多线程的TCPserver的实例作为结束。

三、实现一个ThreadingTCPServer

# server.py
import socketserver
import threading class ThreadingTCPServer(socketserver.ThreadingMixIn,socketserver.TCPServer): #继承ThreadingMixIn,使其支持多线程
pass class myTCPHandle(socketserver.StreamRequestHandler):
def handle(self):
print("connet from %s:%s" % self.client_address)
cur_thread=threading.current_thread() #打印当前处理请求线程的名字
self.data=self.rfile.readline()                        #读取客户端的数据
print(cur_thread.name)
print(str(self.data,encoding="utf-8"))
self.wfile.write(bytes('你好',encoding='utf-8'))            #向客户端发送数据 if __name__=='__main__':
address=('127.0.0.1',9999)
server=socketserver.ThreadingTCPServer(address,myTCPHandle)         #创建一个支持多线程的socket
#server=socketserver.TCPServer(address,myTCPHandle)
server.serve_forever()                               #调用server_forever()处理请求直到被shutdown
'''
服务端打印的数据如下:
  connect from 127.0.0.1:xxx
  Thread-x
  from client:x
  ...
''' #client.py
import socket
for i in range(5):
k=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义了5个套接字,连接服务端,并向他发送数据
k.connect(('127.0.0.1',9999))
senddata='from client:{}\n'.format(i+1)                 #向服务端发送数据
k.send(bytes(senddata,encoding='utf-8'))
data_recive=k.recv(1024)
print(str(data_recive,encoding='utf-8'))                 #打印从服务端收到的数据 你好
k.close()

到这里我就实现了一个如何处理多线程的TCPServer,程序虽然简单,但是有助于大家理解socketserver,至于多进程的实现也类似,这里就不多叙述了,大家有兴趣的可以去实现以下。

  

Python之socketserver源码分析的更多相关文章

  1. python基础-11 socket,IO多路复用,select伪造多线程,select读写分离。socketserver源码分析

    Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

  2. Python 进阶之源码分析:如何将一个类方法变为多个方法?

    前一篇文章<Python 中如何实现参数化测试?>中,我提到了在 Python 中实现参数化测试的几个库,并留下一个问题: 它们是如何做到把一个方法变成多个方法,并且将每个方法与相应的参数 ...

  3. 解读python中SocketServer源码

    在看SocketServer源码之前,先看一个例子: class Base(object): def __init__(self, name): self.name = name self.Testf ...

  4. python成长之路10——socketserver源码分析

    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 socket.AF_INET ipv4(默认) socket.AF_INE ...

  5. Python SocketServer源码分析

    1      XXXServer 1.1      BaseSever 提供基础的循环等待请求的处理框架.使用serve_forever启动服务,使用shutdown停止.同时提供了一些可自行扩展的方 ...

  6. python SocketServer 源码分析

    附上原文链接: http://beginman.cn/python/2015/04/06/python-SocketServer/

  7. Python之namedtuple源码分析

    namedtuple()函数根据提供的参数创建一个新类,这个类会有一个类名,一些字段名和一个可选的用于定义类行为的关键字,具体实现如下 namedtuple函数源码 from keyword impo ...

  8. python string.py 源码分析 三:maketrans

    l = map(chr, xrange(256)) #将ascii转为字符串 _idmap = str('').join(l) del l # Construct a translation stri ...

  9. python string.py 源码分析 二:capwords

    def capwords(s, sep=None): """capwords(s [,sep]) -> string Split the argument into ...

随机推荐

  1. Environment 类

    提供有关当前环境和平台的信息以及操作它们的方法. 此类不能被继承. using System; using System.Collections; using System.Collections.G ...

  2. iOS中MVC设计模式

    在组织大型项目的代码文件时,我们常用MVC的思想.MVC的概念讲起来非常简单,就和对象(object)一样.但是理解和应用起来却非常困难.今天我们就简单总结一下MVC设计理念. MVC(Model V ...

  3. 在DNS管理器——用局域网IP指定你所起的域名名称

    在服务器上面,进行以下相关的操作: 第一步:打开DNS管理器; 第二步:在“正向查找区域”添加域名为:icanyin.net; 第三步:添加主机pm,将域名pm.icanyin.net解析为IP地址: ...

  4. LoadRunner - 当DiscuzNT遇上了Loadrunner(下) (转发)

    当DiscuzNT遇上了Loadrunner(下) 在之前的两篇文章中,基本上介绍了如何录制脚本和生成并发用户,同时还对测试报告中的几个图表做了简单的说明.今天这篇文章做为这个系列的最后一篇,将会介绍 ...

  5. python并行迭代

    并行迭代:同时并行遍历两个列表 for line1,line2 in zip(line1_list, line2_list): ... 无聊,贴一段刚才的代码: import sys import s ...

  6. C++中的链表节点用模板类和用普通类来实现的区别

    C++中的链表节点通常情况下类型都是一致的.因此我们可以用模板来实现. #include <iostream> using namespace std; template<typen ...

  7. LeetCode-Largest Divisble Subset

    Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of ...

  8. 委托、匿名委托和lambda表达式

    1.委托 在.NET中,委托有点类似于C/C++中的函数指针,但与指针不同的是,委托是一种安全的类型,那么我们就以实现两个数的差为例,先声明一个成员方法: public int CompareTwoV ...

  9. MVC中使用SignalR打造酷炫实用的即时通讯功能附源码

    前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯.由于当时走的太急,忘记把代码拿出来.想想这已经是大半年前的事情了,时间过 ...

  10. asp.net中下载文件的问题

    今天解决web的文件下载问题,下载的方法网上很多,不过我的下载有点特殊: 1.下载按钮在gridview中,是模板列的linkButton: 2.使用了ajax控件: 所以,在下载时总是报错,通过查找 ...