zoukankan      html  css  js  c++  java
  • socketserver实现并发及源码分析

    import socketserver
    '''服务端'''
    '''tcp版'''
    # class Myserver(socketserver.BaseRequestHandler): # 必须继承BaseRequestHandler这个类,目的是调用自定义handle()方法
    #  def handle(self): # 必须自定义一个handle()方法,
    #     print('conn is:', self.request) # self.request=conn
    #     print('addr is:', self.client_address) # self.client_address=addr
    #
    #     while True: # 通信循环
    #        try:
    #           data = self.request.recv(1024)
    #           if not data: break
    #           print('客户端发送的消息:', data.decode('utf-8'), self.client_address)
    #           self.request.sendall(data.upper())
    #        except Exception:
    #           break
    #
    # if __name__ == '__main__':
    #  s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), Myserver) # 多线程的tcp服务端,Myserver相当于是循环链接里套的一个通信循环
    #  # 相当于从socket()实例化到listen()这个步骤
    #  # s = socketserver.ForkingTCPServer(('127.0.0.1', 8080), Myserver) # 多进程,系统开销高于多线程,一般用多线程,多进程win系统无法运行
    #  print(s.server_address) # ('127.0.0.1', 8080)
    #  print(s.RequestHandlerClass) # <class '__main__.Myserver'>
    #  print(Myserver) # <class '__main__.Myserver'>
    #  s.serve_forever()
    
    # 文件一运行,首先运行的是socketserver.ThreadingTCPServer(),socketserver.ThreadingTCPServer()是一个类实例化的过程,ctrl+左键查看
    # 其源码,先继承了ThreadingMixIn,然后继承了TCPServer;ThreadingMixIn没有__init__方法,那么去找TCPServer,TCPServer定义了4个类数据
    # 属性,分别代表套接字家族、tcp协议链接,半链接池及是否重用端口(setsocketopt),TCPServer实例化需要3个参数,有一个是默认参数;('127.0.0.1', 8080)
    # 参数给了server_address,Myserver参数给了RequestHandlerClass;TCPServer的__init__中有继承父类BaseServer的__init__,且把server_address
    # 和RequestHandlerClass传给了父类BaseServer的__init__;在BaseServer中的__init__,self.server_address = server_address,self.RequestHandlerClass = RequestHandlerClass,
    # 也就是说s.server_address=('127.0.0.1', 8080),s.RequestHandlerClass=Myserver;再回到TCPServer的__init__,self.socket就是在实例化socket()①,
    # 还有一段if代码,bind_and_activate默认为True,此时执行的是try代码块,代码块执行了两个方法self.server_bind()和self.server_activate(),
    # 一个是绑定ip及端口②,一个是设置半链接池(数量默认为5)③,到这里实例化就已经结束了,可以看到这也是一个完整的tcp建立链接之前的流程;
    
    # s.serve_forever(),此方法是在BaseServer中,运行self._handle_request_noblock()在BaseServer,运行self.get_request()在TCPServer,
    # 然后运行self.socket.accept(),也就是建立链接,得到两个结果,赋值给了request, client_address;回到self._handle_request_noblock()运行self.process_request(request, client_address),
    # self.process_request(request, client_address)在BaseServer,运行self.finish_request(request, client_address),
    # finish_request(self, request, client_address)在BaseServer,运行self.RequestHandlerClass(request, client_address, self),
    # 之前讲到socketserver.ThreadingTCPServer()实例化时Myserver参数给了RequestHandlerClass,那么self.RequestHandlerClass(request, client_address, self)
    # 相当于是在实例化自己定义的Myserver类;继承了BaseRequestHandler,BaseRequestHandler的__init__需要3个参数,self.request = request,
    # self.client_address = client_address,self.server = s,还有try代码块,运行self.handle(),此时的handle()运行的是自己定义的Myserver类
    # 中的handle()方法,实现了循环通信
    
    
    '''udp版'''
    class Myserver_1(socketserver.BaseRequestHandler):
       def handle(self):
             print(self.request)
             # (b'qwe', <socket.socket fd=380, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
             # self.request结果为元组类型,第一个是收到客户端字节类型的消息;第二个是socket()对象,可以根据此来进行发消息通信
             data = self.request[0]
             print('客户端发送的消息是:', data.decode('utf-8'))
             self.request[1].sendto(data.upper(), self.client_address) # self.client_address是发送方地址信息
    
    if __name__ == '__main__':
       s1 = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), Myserver_1)
       s1.serve_forever()
    
    
    # 源码分析总结:
    # 1.基于tcp的socketserver我们自己定义的类中的
    # self.server即套接字对象
    # self.request即一个链接
    # self.client_address即客户端地址
    # 2.基于udp的socketserver我们自己定义的类中的
    # self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
    # self.client_address即客户端地址
    '''客户端1'''
    from socket import *
    
    ip_port = ('127.0.0.1', 8080)
    buffer_size = 1024
    
    '''tcp'''
    # tcp_client = socket(AF_INET, SOCK_STREAM)
    #
    # tcp_client.connect(ip_port)
    #
    # while True:
    #  data = input('请输入:').strip()
    #  if not data: continue
    #  if data == 'quit': break
    #  tcp_client.sendall(data.encode('utf-8'))
    #  msg = tcp_client.recv(buffer_size)
    #  print('服务端发送的消息:', msg.decode('utf-8'))
    # tcp_client.close()
    
    
    '''udp'''
    udp_client = socket(AF_INET, SOCK_DGRAM)
    
    while True:
       msg = input('请输入:').strip()
    
       udp_client.sendto(msg.encode('utf-8'), ip_port)
    
       data, addr = udp_client.recvfrom(buffer_size)
    
       print('服务端回复的消息是:', data.decode('utf-8'))
    '''客户端2'''
    from socket import *
    
    ip_port = ('127.0.0.1', 8080)
    buffer_size = 1024
    
    '''tcp'''
    # tcp_client = socket(AF_INET, SOCK_STREAM)
    #
    # tcp_client.connect(ip_port)
    #
    # while True:
    #  data = input('请输入:').strip()
    #  if not data: continue
    #  if data == 'quit': break
    #  tcp_client.sendall(data.encode('utf-8'))
    #  msg = tcp_client.recv(buffer_size)
    #  print('服务端发送的消息:', msg.decode('utf-8'))
    # tcp_client.close()
    
    
    '''udp'''
    udp_client = socket(AF_INET, SOCK_DGRAM)
    
    while True:
       msg = input('请输入:').strip()
    
       udp_client.sendto(msg.encode('utf-8'), ip_port)
    
       data, addr = udp_client.recvfrom(buffer_size)
    
       print('服务端回复的消息是:', data.decode('utf-8'))

    类继承顺序图示

    # server类:处理链接
    # BaseServer
    # TCPServer
    # UDPServer
    # UnixStreamServer(Unix平台)
    # UnixDatagramServer(Unix平台)

    # request类:处理通信
    # BaseRequestHandler
    # StreamRequestHandler
    # DatagramRequestHandler

    while True: print('studying...')
  • 相关阅读:
    node.js学习二---------------------同步API和异步API的区别
    node.js学习一---------------------模块的导入
    ES6函数的特性(箭头语法)
    10分钟了解Android的Handler机制
    10分钟了解Android的事件分发
    SwipeRefreshLayout,用最少的代码定制最美的上下拉刷新样式
    手把手教你React Native 实战之开山篇《一》
    Android 组件化方案探索与思考
    2018谷歌I/O开发者大会8大看点汇总 新品有哪些
    Glide高级详解—缓存与解码复用
  • 原文地址:https://www.cnblogs.com/xuewei95/p/14808946.html
Copyright © 2011-2022 走看看