• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    分析python并发网络通信模型

    一、常见模型分类

    1.1、循环服务器模型

    循环接收客户端请求,处理请求。同一时刻只能处理一个请求,处理完毕后再处理下一个。

    1.2、IO并发模型

    利用IO多路复用,异步IO等技术,同时处理多个客户端IO请求。

    1.3、多进程/线程网络并发模型

    每当一个客户端连接服务器,就创建一个新的进程/线程为该客户端服务,客户端退出时再销毁该进程/线程。

    二、基于fork的多进程网络并发模型

    1.创建监听套接字

    2.等待接收客户端请求

    3.客户端连接创建新的进程处理客户端请求

    4.原进程继续等待其他客户端连接

    5.如果客户端退出,则销毁对应的进程

    from socket import *
    import os
    import signal
    
    # 创建监听套接字
    HOST = '0.0.0.0'
    PORT = 8888
    ADDR = (HOST,PORT)
    
    # 客户端服务函数
    def handle(c):
      while True:
        data = c.recv(1024)
        if not data:
          break
        print(data.decode())
        c.send(b'OK')
      c.close()
    
    s = socket()  # tcp套接字
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)   # 设置套接字端口重用
    s.bind(ADDR)
    s.listen(3)
    
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)    # 处理僵尸进程
    
    print("Listen the port %d..." % PORT)
    
    # 循环等待客户端连接
    while True:
      try:
        c,addr = s.accept()
      except KeyboardInterrupt:
        os._exit(0)
      except Exception as e:
        print(e)
        continue
    
      # 创建子进程处理这个客户端
      pid = os.fork()
      if pid == 0:  # 处理客户端请求
        s.close()
        handle(c)
        os._exit(0)  # handle处理完客户端请求子进程也退出
    
      # 无论出错或者父进程都要循环回去接受请求
      # c对于父进程没用
      c.close()

    三、基于threading的多线程网络并发

    1.创建监听套接字

    2.循环接收客户端连接请求

    3.当有新的客户端连接创建线程处理客户端请求

    4.主线程继续等待其他客户端连接

    5.当客户端退出,则对应分支线程退出

    from socket import *
    from threading import Thread
    import sys
    
    # 创建监听套接字
    HOST = '0.0.0.0'
    PORT = 8888
    ADDR = (HOST,PORT)
    
    # 处理客户端请求
    def handle(c):
      while True:
        data = c.recv(1024)
        if not data:
          break
        print(data.decode())
        c.send(b'OK')
      c.close()
    
    s = socket()  # tcp套接字
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(ADDR)
    s.listen(3)
    
    print("Listen the port %d..."%PORT)
    # 循环等待客户端连接
    while True:
      try:
        c,addr = s.accept()
      except KeyboardInterrupt:
        sys.exit("服务器退出")
      except Exception as e:
        print(e)
        continue
    
      # 创建线程处理客户端请求
      t = Thread(target=handle, args=(c,))
      t.setDaemon(True)   # 父进程结束则所有进程终止
      t.start()

    四、ftp 文件服务器

    4.1、项目功能

    客户端有简单的页面命令提示:功能包含:

    服务器需求 :

    技术分析:

    粘包的处理

    4.2、整体结构设计

    ftp server:

    from socket import *
    from threading import Thread
    import os
    import time
    
    # 全局变量
    HOST = '0.0.0.0'
    PORT = 8080
    ADDR = (HOST,PORT)
    FTP = "/home/tarena/FTP/"  # 文件库位置
    
    # 创建文件服务器服务端功能类
    class FTPServer(Thread):
      def __init__(self,connfd):
        self.connfd = connfd
        super().__init__()
    
      def do_list(self):
        # 获取文件列表
        files = os.listdir(FTP)
        if not files:
          self.connfd.send("文件库为空".encode())
          return
        else:
          self.connfd.send(b'OK')
          time.sleep(0.1)  # 防止和后面发送内容粘包
    
        # 拼接文件列表
        files_ = ""
        for file in files:
          if file[0] != '.' and \
    
                  os.path.isfile(FTP+file):
            files_ += file + '\n'
        self.connfd.send(files_.encode())
    
      def do_get(self,filename):
        try:
          fd = open(FTP+filename,'rb')
        except Exception:
          self.connfd.send("文件不存在".encode())
          return
        else:
          self.connfd.send(b'OK')
          time.sleep(0.1)
        # 文件发送
        while True:
          data = fd.read(1024)
          if not data:
            time.sleep(0.1)
            self.connfd.send(b'##')
            break
          self.connfd.send(data)
    
      # 循环接收客户端请求
      def run(self):
        while True:
          data = self.connfd.recv(1024).decode()
          if not data or data == 'Q':
            return 
          elif data == 'L':
            self.do_list()
          elif data[0] == 'G':   # G filename
            filename = data.split(' ')[-1]
            self.do_get(filename)
    
    # 网络搭建
    def main():
      # 创建套接字
      sockfd = socket()
      sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
      sockfd.bind(ADDR)
      sockfd.listen(3)
      print("Listen the port %d..."%PORT)
      while True:
        try:
          connfd,addr = sockfd.accept()
          print("Connect from",addr)
        except KeyboardInterrupt:
          print("服务器程序退出")
          return
        except Exception as e:
          print(e)
          continue
    
        # 创建新的线程处理客户端
        client = FTPServer(connfd)
        client.setDaemon(True)
        client.start()   # 运行run方法
    
    
    if __name__ == "__main__":
      main()

    ftp client:

    from socket import *
    import sys
    
    ADDR = ('127.0.0.1',8080) # 服务器地址
    
    # 客户端功能处理类
    class FTPClient:
      def __init__(self,sockfd):
        self.sockfd = sockfd
    
      def do_list(self):
        self.sockfd.send(b'L')  # 发送请求
        # 等待回复
        data = self.sockfd.recv(128).decode()
        if data == 'OK':
          # 一次接收文件列表字符串
          data = self.sockfd.recv(4096)
          print(data.decode())
        else:
          print(data)
    
      def do_get(self,filename):
        # 发送请求
        self.sockfd.send(('G '+filename).encode())
        # 等待回复
        data = self.sockfd.recv(128).decode()
        if data == 'OK':
          fd = open(filename,'wb')
          # 接收文件
          while True:
            data = self.sockfd.recv(1024)
            if data == b'##':
              break
            fd.write(data)
          fd.close()
        else:
          print(data)
    
      def do_quit(self):
        self.sockfd.send(b'Q')
        self.sockfd.close()
        sys.exit("谢谢使用")
    
    # 创建客户端网络
    def main():
      sockfd = socket()
      try:
        sockfd.connect(ADDR)
      except Exception as e:
        print(e)
        return
    
      ftp = FTPClient(sockfd) # 实例化对象
    
      # 循环发送请求
      while True:
        print("\n=========命令选项==========")
        print("****      list         ****")
        print("****    get file       ****")
        print("****    put file       ****")
        print("****      quit         ****")
        print("=============================")
    
        cmd = input("输入命令:")
    
        if cmd.strip() == 'list':
          ftp.do_list()
        elif cmd[:3] == 'get':
          # get filename
          filename = cmd.strip().split(' ')[-1]
          ftp.do_get(filename)
        elif cmd[:3] == 'put':
          # put ../filename
          filename = cmd.strip().split(' ')[-1]
          ftp.do_put(filename)
        elif cmd.strip() == 'quit':
          ftp.do_quit()
        else:
          print("请输入正确命令")
    
    
    
    if __name__ == "__main__":
      main()

    五、IO并发

    定义:在内存中数据交换的操作被定义为IO操作,IO------输入输出

    内存和磁盘进行数据交换: 文件的读写 数据库更新

    内存和终端数据交换 :input print sys.stdin sys.stdout sys.stderr

    内存和网络数据的交换: 网络连接 recv send recvfrom

    IO密集型程序 : 程序执行中有大量的IO操作,而较少的cpu运算操作。消耗cpu较少,IO运行时间长

    CPU(计算)密集型程序:程序中存在大量的cpu运算,IO操作相对较少,消耗cpu大。

    5.1、IO分类

    IO分为:阻塞IO、非阻塞IO、IO多路复用、事件驱动IO、异步IO

    阻塞IO

    阻塞情况:

    非阻塞IO

    定义 : 通过修改IO属性行为, 使原本阻塞的IO变为非阻塞的状态。

    设置套接字为非阻塞IO

    超时检测 :设置一个最长阻塞时间,超过该时间后则不再阻塞等待。

    5.2、IO多路复用

    定义 :通过一个监测,可以同时监控多个IO事件的行为。当哪个IO事件可以执行,即让这个IO事件发生。

    rs, ws, xs = select(rlist, wlist, xlist[, timeout])  监控IO事件,阻塞等待监控的IO时间发生

    参数 :

    返回值:

    select 实现tcp服务

    1.将关注的IO放入对应的监控类别列表

    2.通过select函数进行监控

    3.遍历select返回值列表,确定就绪IO事件

    4.处理发生的IO事件

    from socket import *
    from select import select
    
    # 创建一个监听套接字作为关注的IO
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('0.0.0.0',8888))
    s.listen(3)
    
    # 设置关注列表
    rlist = [s]
    wlist = []
    xlist = [s]
    
    # 循环监控IO
    while True:
      rs,ws,xs = select(rlist,wlist,xlist)
      # 遍历三个返回列表,处理IO
      for r in rs:
        # 根据遍历到IO的不同使用if分情况处理
        if r is s:
          c,addr = r.accept()
          print("Connect from",addr)
          rlist.append(c) # 增加新的IO事件
        # else为客户端套接字就绪情况
        else:
          data = r.recv(1024)
          # 客户端退出
          if not data:
            rlist.remove(r) # 从关注列表移除
            r.close()
            continue # 继续处理其他就绪IO
          print("Receive:",data.decode())
          # r.send(b'OK')
          # 我们希望主动处理这个IO对象
          wlist.append(r)
    
      for w in ws:
        w.send(b'OK')
        wlist.remove(w) # 使用后移除
    
      for x in xs:
        pass

    注意:

    5.3、位运算

    将整数转换为二进制, 按照二进制位进行运算符操作
    按位与   | 按位或   ^ 按位异或   左移 >> 右移
    11 1011    14 1110
    (11 14 1010)   (11| 14 1111)  (11^ 14 0101)
    11 2 ===> 44 右侧补0    14 >> 2 ===> 3 挤掉右侧的数字

    使用 :

    5.4、poll方法实现IO多路复用

    创建poll对象:p = select.poll()

    注册关注的IO事件:p.register(fd,event)

    常用类型:

    取消对IO的关注:p.unregister(fd)

    参数: IO对象或者IO对象的fileno

    events = p.poll():

    events是一个列表 [(fileno,evnet),(),()....]

    每个元组为一个就绪IO,元组第一项是该IO的fileno,第二项为该IO就绪的事件类型

    poll_server 步骤

    1.创建套接字

    2.将套接字register

    3.创建查找字典,并维护

    4.循环监控IO发生

    5.处理发生的IO

    from socket import *
    from select import *
    
    # 创建套接字
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('0.0.0.0',8888))
    s.listen(3)
    
    # 创建poll对象关注s
    p = poll()
    
    # 建立查找字典,用于通过fileno查找IO对象
    fdmap = {s.fileno():s}
    
    # 关注s
    p.register(s,POLLIN|POLLERR)
    
    # 循环监控
    while True:
      events = p.poll()
      # 循环遍历发生的事件 fd-->fileno
      for fd,event in events:
        # 区分事件进行处理
        if fd == s.fileno():
          c,addr = fdmap[fd].accept()
          print("Connect from",addr)
          # 添加新的关注IO
          p.register(c,POLLIN|POLLERR)
          fdmap[c.fileno()] = c # 维护字典
        # 按位与判定是POLLIN就绪
        elif event  POLLIN:
          data = fdmap[fd].recv(1024)
          if not data:
            p.unregister(fd) # 取消关注
            fdmap[fd].close()
            del fdmap[fd]  # 从字典中删除
            continue
          print("Receive:",data.decode())
          fdmap[fd].send(b'OK')

    5.5、epoll方法

    1. 使用方法 : 基本与poll相同

    2. epoll特点

    from socket import *
    from select import *
    
    # 创建套接字
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('0.0.0.0',8888))
    s.listen(3)
    
    # 创建epoll对象关注s
    ep = epoll()
    
    # 建立查找字典,用于通过fileno查找IO对象
    fdmap = {s.fileno():s}
    
    # 关注s
    ep.register(s,EPOLLIN|EPOLLERR)
    
    # 循环监控
    while True:
      events = ep.poll()
      # 循环遍历发生的事件 fd-->fileno
      for fd,event in events:
        print("亲,你有IO需要处理哦")
        # 区分事件进行处理
        if fd == s.fileno():
          c,addr = fdmap[fd].accept()
          print("Connect from",addr)
          # 添加新的关注IO
          # 将触发方式变为边缘触发
          ep.register(c,EPOLLIN|EPOLLERR|EPOLLET)
          fdmap[c.fileno()] = c # 维护字典
        # 按位与判定是EPOLLIN就绪
        # elif event  EPOLLIN:
        #   data = fdmap[fd].recv(1024)
        #   if not data:
        #     ep.unregister(fd) # 取消关注
        #     fdmap[fd].close()
        #     del fdmap[fd]  # 从字典中删除
        #     continue
        #   print("Receive:",data.decode())
        #   fdmap[fd].send(b'OK')

    以上就是分析python并发网络通信模型的详细内容,更多关于python 并发网络通信模型的资料请关注脚本之家其它相关文章!

    您可能感兴趣的文章:
    • Python实现socket库网络通信套接字
    • Python中socket网络通信是干嘛的
    • 教你使用Python建立任意层数的深度神经网络
    • Python利用PyQt5制作一个获取网络实时数据NBA数据播报GUI功能
    • python网络通信图文详解
    上一篇:浅谈Python Pygame图像的基本使用
    下一篇:整理Python中常用的conda命令操作
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    分析python并发网络通信模型 分析,python,并发,网络通信,