python socketserver模块_Python-SocketServer模块

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 classBaseServer:3   def __init__(self, server_address, RequestHandlerClass):4     """Constructor. May be extended, do not override."""

5     self.server_address =server_address6     self.RequestHandlerClass =RequestHandlerClass7     self.__is_shut_down =threading.Event()8     self.__shutdown_request =False9   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 classBaseRequestHandler:3   def __init__(self, request, client_address, server):4     self.request =request5     self.client_address =client_address6     self.server =server7     self.setup()8   try:9     self.handle()10   finally:11     self.finish()12   def setup(self): #每一个连接初始化

13     pass

14   def handle(self): #每一次请求处理

15     pass

16   def finish(self): #每一个连接清理

17     pass

测试:

1 importthreading2 importsocketserver3

4 classMyHandler(socketserver.BaseRequestHandler):5 defhandle(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的socket

14

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) #生成一个多线程的server

21 print(server.__class__.__name__)22

23

24 server.serve_forever() #永久开启

测试

结果:

1 D:\python3.7\python.exe E:/code_pycharm/复习/t4.py2 ThreadingTCPServer3 ------------------------------

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 started>, ]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 importthreading2 importsocketserver3

4 classMyHandler(socketserver.BaseRequestHandler):5 defhandle(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的socket

15

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) #生成一个多线程的server

27

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 started>, ]

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方法 执行完了,才能处理另一个连接,且只有主线程。

总结:

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

从BaseRequestHandler类派生出子类,并覆盖器handler() 方法来创建请求处理程序类,此方法将处理传入请求

实例化一个服务器类,传参服务器的地址和请求处理类

调用服务器实例的handle_request(),或server_forever() 方法

调用server_close()关闭套接字

4、测试实例

4.1、显示EchoServer

顾名思义,回显消息

1 importthreading2 from socketserver importThreadingTCPServer, TCPServer, BaseRequestHandler3 importsys4

5 classEchoHandler(BaseRequestHandler):6 defsetup(self):7 super().setup()8 self.event = threading.Event() #初始化工作

9

10 deffinish(self):11 super().finish()12 self.event.set() #清理工作

13

14 defhandle(self):15 super().handle()16

17 try:18 while notself.event.is_set():19 data = self.request.recv(1024).decode()20 msg = '{}{}'.format(self.client_address, data).encode()21 self.request.send(msg)22 exceptException 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 whileTrue:37 cmd = input('>>')38 if cmd.strip() == 'quit':39 server.shutdown()40 break

41 print(threading.enumerate())42 exceptException as e:43 print(e)44 exceptKeyboardInterrupt:45 pass

46 finally:47 print('exit')48 sys.exit(0)

View Code

4.2、实现群聊

1 importthreading2 from socketserver importThreadingTCPServer, BaseRequestHandler3 importsys4 importlogging5

6 FOMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'

7 logging.basicConfig(level=logging.INFO, format=FOMAT)8

9 classChatServerHanlder(BaseRequestHandler):10 clients ={}11

12

13 defsetup(self):14 super().setup()15 self.event =threading.Event()16 self.clients[self.request] =self.client_address17

18 deffinish(self):19 super().finish()20 self.clients.pop(self.request)21 self.event.set()22 print(self.clients)23

24

25

26 defhandle(self):27 super().handle()28

29 while notself.event.is_set():30 data = self.request.recv(1024)31

32 if data.strip() == b'quit' or data == b'':33 break

34

35 msg = 'your msg is {}'.format(data.decode()).encode()36

37 for s inself.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 whileTrue:48 cmd = input(">>>>")49 if cmd == 'quit':50 server.shutdown()51 break

52 print(threading.enumerate())53 exceptException as e:54 print(e)55

56 exceptKeyboardInterrupt:57 pass

58 finally:59 print('exit')60 sys.exit(0)

View Code

总结:

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值