zoukankan      html  css  js  c++  java
  • Socket网络编程-SocketServer

                Socket网络编程-SocketServer

                                       作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.SocketServer概述

      socket编程过于底层,编程虽然有套路,但是想要写出健壮的代码还是比较困难的,所以很多语言都对 socket底层API进行封装,Python的封装就是socketserver模块。它是网络服务编程框架,便于企业级 快速开发。
    
      类的继承关系如下所示: 
        +------------+
        | BaseServer |
        +------------+
        |
        |
              v
        +-----------+        +--------------------+
        | UDPServer |------->| UnixDatagramServer |
        +-----------+        +--------------------+
              v
        +-----------+
                             +------------------+
        | TCPServer |------->| UnixStreamServer |
        +-----------+        +------------------+
     
      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是创建多线程。 
    
      fork需要操作系统支持,Windows不支持。

    二.编程接口

    1>.创建服务器需要几个步骤

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

    2>.案例展示

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 import threading
     8 import socketserver
     9 import logging
    10 
    11 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
    12 logging.basicConfig(format=FORMAT, level=logging.INFO)
    13 
    14 """
    15 BaseRequestHandler:
    16     def __init__(self, request, client_address, server):
    17         self.request = request
    18         self.client_address = client_address
    19         self.server = server
    20         self.setup()
    21         try:
    22             self.handle()
    23         finally:
    24             self.finish()
    25 
    26 参数说明:
    27     它是和用户连接的用户请求处理类的基类
    28     服务端Server实例接收用户请求后,最后会实例化这个类。
    29     它被初始化时,送入3个构造参数:request, client_address, server自身 
    30     以后就可以在BaseRequestHandler类的实例上使用以下属性:
    31         self.request是和客户端的连接的socket对象 
    32         self.server是TCPServer实例本身 
    33         self.client_address是客户端地址
    34     这个类在初始化的时候,它会依次调用3个方法。子类可以覆盖这些方法。
    35 """
    36 class MyHandler(socketserver.BaseRequestHandler):
    37     def handle(self):
    38         # super().handle()                                        #可以不调用,父类handle什么都没有做
    39         print('-'*30)
    40         print(self.server)                                       #服务
    41         print(self.request)                                      #服务端负责客户端连接请求的socket对象
    42         print(self.client_address)                               #客户端地址
    43         print(self.__dict__)
    44         print(self.server.__dict__)                              #能看到负责accept的socket
    45         print(threading.enumerate())
    46         print(threading.current_thread())
    47         print('-'*30)
    48         for i in range(3):
    49             data = self.request.recv(1024)
    50             logging.info(data)
    51         logging.info('====end====')
    52 
    53 addr = ('172.30.1.2', 9999)
    54 
    55 """
    56     将ThreadingTCPServer换成TCPServer,同时连接2个客户端观察效果。 ThreadingTCPServer是异步的,可以同时处理多个连接。
    57     TCPServer是同步的,一个连接处理完了,即一个连接的handle方法执行完了,才能处理另一个连接, 且只有主线程。
    58 """
    59 server = socketserver.ThreadingTCPServer(addr, MyHandler)       #注意参数是MyHandler类
    60 server.serve_forever()                                          #永久循环执行

      

    三.实现EchoServer(顾名思义,Echo,来什么消息回显什么消息 客户端发来什么信息,返回什么信息) 

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 import threading
     8 import socketserver
     9 
    10 class Handler(socketserver.BaseRequestHandler):
    11     def setup(self):
    12         super().setup()
    13         self.event = threading.Event()
    14         
    15     def finish(self):
    16         super().finish()
    17         self.event.set()
    18         
    19     def handle(self):
    20         super().handle()
    21         print('-' * 30)
    22         while not self.event.is_set():
    23             data = self.request.recv(1024).decode()
    24             print(data)
    25             msg = '{} {}'.format(self.client_address, data).encode()
    26             self.request.send(msg)
    27 
    28 server = socketserver.ThreadingTCPServer(('172.30.1.2', 9999), Handler)
    29 print(server)
    30 threading.Thread(target=server.serve_forever, name='EchoServer', daemon=True).start()
    31 
    32 while True:
    33     cmd = input('>>')
    34     if cmd == 'quit':
    35         server.server_close()
    36         break
    37     print(threading.enumerate())

    四.实战—改写ChatServer 

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 import datetime
     8 import threading
     9 from socketserver import ThreadingTCPServer, StreamRequestHandler
    10 import logging
    11 
    12 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
    13 logging.basicConfig(format=FORMAT, level=logging.INFO)
    14 
    15 """
    16     注意:此程序线程不安全
    17 """
    18 class ChatHandler(StreamRequestHandler):
    19     clients = {}
    20 
    21     def setup(self):
    22         super().setup()
    23         self.event = threading.Event()
    24         self.clients[self.client_address] = self.wfile
    25 
    26     def handle(self):
    27         super().handle()
    28         # for k,v in self.__dict__.items():
    29         #     print(k, type(v), v)
    30 
    31         while not self.event.is_set():
    32             data = self.rfile.read1(1024) # 可以读取到数据
    33             data = data.decode().rstrip()
    34             print(data, '~~~~~~~~~~~~~')
    35 
    36             if data == 'quit' or data == '': # 主动退出和断开
    37                 break
    38 
    39             msg = '{} {}:{} {}'.format(datetime.datetime.now(), *self.client_address,data)
    40 
    41             for f in self.clients.values():
    42                 f.write(msg.encode())
    43                 f.flush()
    44 
    45     def finish(self):
    46         self.clients.pop(self.client_address)
    47         super().finish()
    48         self.event.set()
    49 
    50 server = ThreadingTCPServer(('172.30.1.2', 9999), ChatHandler)
    51 server.daemon_threads = True # 让所有启动线程都为daemon
    52 
    53 threading.Thread(target=server.serve_forever, name='chatserver', daemon=True).start()
    54 
    55 while True:
    56     cmd = input('>>')
    57     if cmd.strip() == 'quit':
    58         server.server_close()
    59         break
    60     print(threading.enumerate())

    五.总结

      为每一个连接提供RequestHandlerClass类实例,依次调用setup、handle、finish方法,且使用了try...finally结构保证finish方法一定能被调用。这些方法依次执行完成,如果想维持这个连接和客户端 通信,就需要在handle函数中使用循环。
    
      socketserver模块提供的不同的类,但是编程接口是一样的,即使是多进程、多线程的类也是一样,大 大减少了编程的难度。 将socket编程简化,只需要程序员关注数据处理本身,实现Handler类就行了。这种风格在Python十分常见。
  • 相关阅读:
    windows7系统笔记本设置成虚拟WiFi热点(即“无线路由器”)
    分布式事务03单体应用分库多数据源改造
    Spring5.2.x01导入idea
    图解CRM(客户关系管理)全流程
    分布式事务03XA, 2PC, 3PC 等理论知识
    分布式事务01课前知识
    tomcat9源码导入idea
    分布式事务02Springtx核心
    DEA Push rejected: Push to origin/master was rejected问题的解决方法
    Error:gradleresourcestest:thymeleafinaction.main: java.lang.NoClassDefFoundError: org/apache/tools/ant/util/ReaderInputStream
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11980372.html
Copyright © 2011-2022 走看看