博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python-SocketServer模块
阅读量:4948 次
发布时间:2019-06-11

本文共 9391 字,大约阅读时间需要 31 分钟。

1、SocketServer:

  socket编程过于底层,编程虽然有套路,但是想要写出健壮的代码还是比较困难的,所以很多语言都对socket底层

  API进行封装,Python的封装就是——socketserver模块。它是网络服务编程框架,便于企业级快速开发

2、类的继承关系:

+------------+| BaseServer |+------------+        |        v+-----------+              +------------------+| TCPServer |------->| UnixStreamServer |+-----------+             +------------------+        |        v+-----------+              +--------------------+| UDPServer |------->| UnixDatagramServer |+-----------+                +--------------------+

   SocketServer简化了网络服务器的编写。

    它有4个同步类:

        TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。

    2个Mixin类:  

        ForkingMixIn 和 ThreadingMixIn 类,用来支持异步。

    class ForkingUDPServer(ForkingMixIn, UDPServer): pass

    class ForkingTCPServer(ForkingMixIn, TCPServer): pass
    class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
    class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
    fork是创建多进程,thread是创建多线程

3、编程接口:

  socketserver.BaseServer(server_address, RequestHandlerClass)

  需要提供服务器绑定的地址信息,和用于处理请求的RequestHandlerClass类。
  RequestHandlerClass类必须是BaseRequestHandler类的子类,在BaseServer中代码如下:

1 # BaseServer代码 ----》 2 class BaseServer: 3   def __init__(self, server_address, RequestHandlerClass): 4     """Constructor. May be extended, do not override.""" 5     self.server_address = server_address 6     self.RequestHandlerClass = RequestHandlerClass 7     self.__is_shut_down = threading.Event() 8     self.__shutdown_request = False 9   def finish_request(self, request, client_address): # 处理请求的方法10     """Finish one request by instantiating RequestHandlerClass."""11     self.RequestHandlerClass(request, client_address, self) # RequestHandlerClass构造

 

  BaseRequestHandler类

    它是和用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request, client_address, server)

  服务端Server实例接收用户请求后,最后会实例化这个类。

  它被初始化时,送入3个构造参数:request, client_address, server自身
  以后就可以在BaseRequestHandler类的实例上使用以下属性:
  self.request是和客户端的连接的socket对象
  self.server是TCPServer实例本身
  self.client_address是客户端地址‘

  这个类在初始化的时候,它会依次调用3个方法。子类可以覆盖这些方法。

1 # BaseRequestHandler要子类覆盖的方法 2 class BaseRequestHandler: 3   def __init__(self, request, client_address, server): 4     self.request = request 5     self.client_address = client_address 6     self.server = server 7     self.setup() 8   try: 9     self.handle()10   finally:11     self.finish()12   def setup(self): # 每一个连接初始化13     pass14   def handle(self): # 每一次请求处理15     pass16   def finish(self): # 每一个连接清理17     pass

 

  测试:

1 import threading 2 import socketserver 3  4 class MyHandler(socketserver.BaseRequestHandler): 5     def handle(self): 6         # super().handle() # 可以不调用,父类handle什么都没有做,一般习惯性的调用一下父类的 7         print('-'*30) 8         print(self.__dict__) 9         print(self.server) # 服务10         print(self.request) # 服务端负责客户端连接请求的socket对象11         print(self.client_address) # 客户端地址12         print(self.__dict__)13         print(self.server.__dict__) # 能看到负责accept的socket14 15         print(threading.enumerate())16         print(threading.current_thread())17         print('-'*30)18 19 addr = ('127.0.0.1', 9999)20 server = socketserver.ThreadingTCPServer(addr, MyHandler) # 生成一个多线程的server21 print(server.__class__.__name__)22 23 24 server.serve_forever() # 永久开启
测试

  结果:

1 D:\python3.7\python.exe E:/code_pycharm/复习/t4.py 2 ThreadingTCPServer 3 ------------------------------ 4 {
'request':
, 'client_address': ('127.0.0.1', 63665), 'server':
} 5
6
7 ('127.0.0.1', 63665) 8 {
'request':
, 'client_address': ('127.0.0.1', 63665), 'server':
} 9 { 'server_address': ('127.0.0.1', 9999), 'RequestHandlerClass':
, '_BaseServer__is_shut_down':
, '_BaseServer__shutdown_request': False, 'socket':
, '_threads': [
]}10 [<_MainThread(MainThread, started 8048)>,
]11
12 ------------------------------13 14 Process finished with exit code 1
View Code

  测试结果说明:

    handler方法相当于socket的recv方法

    生成的Baseserver 对象(ThreadingTCPServer),就包含了accept,可以通过server.__dict_-可以看到accept的socket

    每个不同的连接上的请求过来后,

      生成这个连接的socket对象,即self.request,

      客户端地址是,self.client_address

    而且没有请求进来,阻塞在server_forever()

    但是,上面的代码,连接后就客户端 立即断开了

  测试:客户端 和 服务器端持久连接

1 import threading 2 import socketserver 3  4 class MyHandler(socketserver.BaseRequestHandler): 5     def handle(self): 6         # super().handle() # 可以不调用,父类handle什么都没有做,一般习惯性的调用一下父类的 7         print('-'*30) 8         print(self.server) # 服务 9 10         # print('=================')11         print(self.request) # 服务端负责客户端连接请求的socket对象12         print(self.client_address) # 客户端地址13         print(self.__dict__)14         print(self.server.__dict__) # 能看到负责accept的socket15 16         print(threading.enumerate())17         print(threading.current_thread())18         print('-'*30)19 20         for i in range(3):21             data = self.request.recv(1024)22             print(data)23             print(' ===== end ==== ')24 25 addr = ('127.0.0.1', 9999)26 server = socketserver.ThreadingTCPServer(addr, MyHandler) # 生成一个多线程的server27 28 server.serve_forever() # 永久开启29 # print(server.__dict__)
添加循环实现

  结果:每次都阻塞在recv,当循环结束,客户端 断开,服务器端一直没有断开

1 D:\python3.7\python.exe E:/code_pycharm/复习/t4.py 2 ------------------------------ 3 
4
5 ('127.0.0.1', 64303) 6 {'request':
, 'client_address': ('127.0.0.1', 64303), 'server':
} 7 {'server_address': ('127.0.0.1', 9999), 'RequestHandlerClass':
, '_BaseServer__is_shut_down':
, '_BaseServer__shutdown_request': False, 'socket':
, '_threads': [
]} 8 [<_MainThread(MainThread, started 6112)>,
] 9
10 ------------------------------11 b'd'12 ===== end ==== 13 b'd'14 ===== end ==== 15 b'd'16 ===== end ====
3次之后断开连接

 

  将ThreadingTCPServer 换成TCPServer, 同时连接2个客户端观察效果发现,是串行的,一个结束之后,才能另外一个处理

  并且每次都是阻塞到 recv 方法处

  ThreadingTCPServer 是异步的,可以同时处理多个请求。

  TCPServer 是同步的,一个连接处理完了,即一个连接的 handle方法 执行完了,才能处理另一个连接,且只有主线程。

  总结:

    创建服务器需要的几个步骤:

    1. 从BaseRequestHandler类派生出子类,并覆盖器handler() 方法来创建请求处理程序类,此方法将处理传入请求
    2. 实例化一个服务器类,传参服务器的地址和请求处理类
    3. 调用服务器实例的handle_request(),或server_forever() 方法
    4. 调用server_close()关闭套接字

4、测试实例

  4.1、显示EchoServer

    顾名思义,回显消息  

1 import threading 2 from  socketserver import ThreadingTCPServer, TCPServer, BaseRequestHandler 3 import  sys 4  5 class EchoHandler(BaseRequestHandler): 6     def setup(self): 7         super().setup() 8         self.event = threading.Event() # 初始化工作 9 10     def finish(self):11         super().finish()12         self.event.set() # 清理工作13 14     def handle(self):15         super().handle()16 17         try:18             while not self.event.is_set():19                 data = self.request.recv(1024).decode()20                 msg = '{}{}'.format(self.client_address, data).encode()21                 self.request.send(msg)22         except Exception as e:23             print(e)24 25         finally:26             print('=== end ====')27 28 29 server = ThreadingTCPServer(('127.0.0.1', 9999), EchoHandler)30 31 server_thread = threading.Thread(target=server.serve_forever, name='EchoServer', daemon=True)32 server_thread.start()33 34 35 try:36     while True:37         cmd = input('>>')38         if cmd.strip() == 'quit':39             server.shutdown()40             break41         print(threading.enumerate())42 except Exception as e:43     print(e)44 except KeyboardInterrupt:45     pass46 finally:47     print('exit')48     sys.exit(0)
View Code

   4.2、实现群聊

1 import threading 2 from socketserver import ThreadingTCPServer, BaseRequestHandler 3 import sys 4 import logging 5  6 FOMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s' 7 logging.basicConfig(level=logging.INFO, format=FOMAT) 8  9 class ChatServerHanlder(BaseRequestHandler):10     clients = {}11 12 13     def setup(self):14         super().setup()15         self.event = threading.Event()16         self.clients[self.request] = self.client_address17 18     def finish(self):19         super().finish()20         self.clients.pop(self.request)21         self.event.set()22         print(self.clients)23 24 25 26     def handle(self):27         super().handle()28 29         while not self.event.is_set():30             data = self.request.recv(1024)31 32             if data.strip() == b'quit' or data == b'':33                 break34 35             msg = ' your msg is {} '.format(data.decode()).encode()36 37             for s in self.clients:38                 s.send(msg)39 40 41 laddr = ('127.0.0.1', 9999)42 server = ThreadingTCPServer(laddr, ChatServerHanlder)43 print(server.socket)44 threading.Thread(target=server.serve_forever, name='server').start()45 46 try:47     while True:48         cmd = input(">>>>")49         if cmd == 'quit':50             server.shutdown()51             break52         print(threading.enumerate())53 except Exception as e:54     print(e)55 56 except KeyboardInterrupt:57     pass58 finally:59     print('exit')60     sys.exit(0)
View Code

 

 

总结:

  为每一个连接提供RequestHandlerClass 类实例,一次调用 setup, handle, finish 方法,且使用了try...finally结构保证finish方法一定鞥呢被调动,这些方法一次执行完成,如果想维持这个连接和客户端通信,就需要在handle函数中使用循环,

  socketserver模块提供的不同的,但是编程接口是一样的,即使多线程,多线程的类也是一样的 ,大大减少了编程难度。

  socket 是由self.request 管理的,不需要自己去close,只需要清理自己定义的一些资源。

  每一个请求,生成一个handler 类的实例。各自操作自己的实例属性,以及公用的类属性

  

 

转载于:https://www.cnblogs.com/JerryZao/p/9900514.html

你可能感兴趣的文章
Spring MVC统一异常处理
查看>>
mybatis爆出constructor和getter异常的解决方法:
查看>>
rest-framework之视图
查看>>
socket技术详解
查看>>
RabbitMQ
查看>>
WordPress中默认文本编辑器替换成百度UEditor编辑器
查看>>
jQuery 删除或是清空某个HTML元素。
查看>>
图片添加水印效果
查看>>
iOS开发UI篇—核心动画(转场动画和组动画)
查看>>
20190724-Python网络数据采集/第 2 章 复杂HTML解析-导航树/正则表达式
查看>>
[Swift]LeetCode605. 种花问题 | Can Place Flowers
查看>>
[Swift]LeetCode494. 目标和 | Target Sum
查看>>
python--斐波那契数列
查看>>
mysql查询练习题
查看>>
python学习笔记(session)
查看>>
vue 与原生app的对接交互(混合开发)
查看>>
JavaEE笔记(七)
查看>>
设计模式--原型模式C++实现
查看>>
[LeetCode] 21. Merge Two Sorted Lists_Easy tag: Linked List
查看>>
[Reactive Programming] Using an event stream of double clicks -- buffer()
查看>>