• IO模型


    基于协程的TCP程序

    from gevent import monkey, spawn
    
    monkey.patch_all()
    import socket
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8888))
    server.listen()
    
    
    def talking(client):
        while True:
            try:
                data = client.recv(1024)
                if not data:
                    client.close()
                    break
                client.send(data.upper())
            except ConnectionResetError:
                client.close()
                break
    
    
    def save():
        while True:
            client, address = server.accept()
            spawn(talking, client)
    
    
    spawn(save).join()
    server
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8888))
    
    while True:
        msg = input('>>>:').strip()
        if not msg: continue
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))
    # import socket
    # from concurrent.futures import ThreadPoolExecutor
    # from threading import current_thread
    #
    # pool = ThreadPoolExecutor(1000)
    #
    #
    # def task():
    #     client = socket.socket()
    #     client.connect(('127.0.0.1', 8888))
    #     while True:
    #         msg = '%s say hi ' % current_thread().name
    #         if not msg: continue
    #         client.send(msg.encode('utf-8'))
    #         data = client.recv(1024)
    #         print(data.decode('utf-8'))
    #
    #
    # for i in range(500):
    #     pool.submit(task)
    client

    协程join的用法

    from gevent import monkey, spawn
    
    monkey.patch_all()
    import time
    
    
    def task1():
        print('task1 run')
        time.sleep(3)
        print('task1 over')
    
    
    def task2():
        print('task2 run')
        time.sleep(3)
        print('task2 over')
    
    
    spawn(task1)
    spawn(task2)
    
    print('over')
    time.sleep(10)
    代码

    IO模型
      模型即套路是解决某个固定问题的方式方法
      IO模型 即解决IO问题的方式方法
      IO指的是输入输出,输入输出设备的速度对比CPU而言是非常慢的,比如recv input 等都属IO操作
      IO操作最大的问题就是会阻塞程序执行
      IO模型要解决的仅仅是网络IO操作
      IO模型有以下几个
    1.阻塞IO
      socket默认就是阻塞的
        问题:同一时间只能服务一个客户端
      方法1:多线程
        优点:如果并发量不高 效率是较高的 因为每一个客户端都有单独线程来处理
        弊端:不可能无限的开启线程 线程也需要占用资源
      方法2:多进程
        优点:可以多个CPU并行处理
        弊端:占用资源非常大,一旦客户端稍微多一点立马就变慢了
      线程池:
        优点:保证了服务器正常稳定运行,还帮你负责创建和销毁线程,以及任务分配
        弊端:一旦并发量超出最大线程数量,就只能等前面的运行完毕
      协程:
        基于单线程并发
        优点:不需要创建一堆线程,不需要在线程间做切换,没有数量限制
        弊端:不能利用多核优势,单核处理器性能也是有上限的,如果真的并发特别da那么处理速度会变慢
    总结:真正导致效率低的是阻塞问题 但是上述几个方法并没有真正解决阻塞问题 仅仅是避开了阻塞问题

    import socket
    
    from threading import Thread
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8888))
    server.listen()
    
    
    def talking(client):
        while True:
            try:
                data = client.recv(1024)
                print('recv...')
                if not data:
                    client.close()
                    break
                    # send是一个本地io操作 速度非常快
                client.send(data.upper())
            except ConnectionResetError:
                client.close()
                break
    
    
    while True:
        client, address = server.accept()
        print('accept')
        t = Thread(target=talking, args=(client,))
        t.start()
    server
    import socket
    import os
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8888))
    print('connect...')
    while True:
        msg = '%s 发来问候 hello ' % os.getpid()
        if not msg: continue
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))
    client

    2.非阻塞IO
      即遇到IO操作也不会导致程序阻塞,会继续执行
      意味着即使遇到IO操作CUP执行权也不会被剥夺
        程序效率就变高了
        弊端:占用CUP太高
        原因是需要无限的循环 去向操作系统拿数据

    import socket
    
    c = socket.socket()
    c.connect(("127.0.0.1", 8888))
    print("connect....")
    
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        c.send(msg.encode("utf-8"))
        data = c.recv(1024)
        print(data.decode("utf-8"))
    client
    import socket
    
    # import time
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8888))
    server.listen()
    # 设置socket是否阻塞 默认为True
    server.setblocking(False)
    # 所有的客户端socket
    cs = []
    # 所有需要返回数据的客户端
    send_cs = []
    
    while True:
        # time.sleep(0.2)
        try:
            client, address = server.accept()  # 三次握手
            print('run accept')
            cs.append(client)  # 存储已经连接成功的客户端
        except BlockingIOError:
            #         没有数据准备 可以作别的事情
            # print('收数据')
            for c in cs[:]:
                try:
                    data = c.recv(1024)
                    if not data:
                        c.close()
                        cs.remove(c)
                    print(data.decode('utf-8'))
                    #                 把数据和连接放进去
                    send_cs.append((c, data))
                    # c.send(data.upper()) io
                # send也是io操作 在一些极端情况下 例如系统缓存满了放不进去 那肯定抛出
                # 非阻塞异常 这时候必须把发送数据 单独拿出来处理 因为recv和send都有可能抛出相同异常
                except BlockingIOError:
                    continue
                except ConnectionResetError:
                    c.close()
                    #                 从所有客户端列表中删除这个连接
                    cs.remove(c)
            # print('发送数据')
            for item in send_cs[:]:
                c, data = item
                try:
                    c.send(data.upper())
                    #                 如果发送成功就把数据从列表中删除
                    send_cs.remove(item)
                except BlockingIOError:  # 如果缓冲区慢了 那就下次再发
                    continue
                except ConnectionResetError:
                    c.close()  # 关闭连接
                    send_cs.remove(item)  # 删除数据
                    #                 从所有客户端中删除这个已经断开的连接
                    cs.remove(c)
    server

    删除列表中的某个数据

    # 方式1
    # li = [1, 2, 3, 4, 5]
    # rm_list = []
    # for i in li:
    #     rm_list.append(i)
    #
    # for i in rm_list:
    #     li.remove(i)
    #
    # print(li)
    # 方式2
    li = [1, 2, 3, 4, 5]
    print(id(li[:]))
    print(id(li))
    # 用切片的方式 产生一个新的列表 新列表中元素与旧列表完全相同
    # 遍历新列表 删除旧列表
    for i in li[:]:
        li.remove(i)
    
    print(li)
    删除列表中的某个元素

    3.IO多路复用 (最重要)

       用一个线程来并发处理所有客户端

      原本我们是直接问操作系统 要数据

        如果是阻塞IO没有数据就进入阻塞状态

        非阻塞IO 没有数据就抛出异常 然后继续询问操作系统

      在多路复用模块中,要先问select 哪些socket已经准备就绪 然后再处理这些已经就绪的socket

      既然是已经就绪 那么执行recv或是send就不会再阻塞

      select模块只有一个函数就是select

      参数1:r_list 需要被select检测是否是可读的客户端 把所有socket放到改列表中,select会负责从中

        找出可以读取数据的socket

      参数2:w_list 需要被select检测是否是可写的客户端 把所有socket放到改列表中,select会负责从中

        找出可以写入数据的socket

      参数3:x_list 存储要检测异常条件。。。忽略即可

      返回一个元组 包含三个列表

        readables 已经处于可读状态的socket即数据已经到达缓冲区

        writeables 已经处于可写状态的socket 即缓冲区没满 可以发送。。。

        x_list :忽略

      从可读或写入列表中拿出所有的socket依次处理它们即可

      也是单线程并发处理所有请求
      与非阻塞不同之处在于不需要频繁不断发送系统调用
      只要等待select选择出准备就绪socket然后进行处理即可

      如果说没有任何一个socket准备就绪 select就会被阻塞住

    import socket
    
    c = socket.socket()
    c.connect(("127.0.0.1", 8888))
    print("connect....")
    
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        c.send(msg.encode("utf-8"))
        data = c.recv(1024)
        print(data.decode("utf-8"))
    client
    import socket
    # import time
    import select
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8888))
    server.listen()
    # 在多路复用中 一旦select交给你一个socket 一定意味着该socket已经准备就绪
    # server.setblocking(False)
    r_list = [server]
    w_list = []
    # 存储需要发送的数据 以及对应的socket 把socket作为key数据作为value
    data_dic = {}
    while True:
        readables, writeables, _ = select.select(r_list, w_list, [])
        # 接收数据 以及服务器创立连接
        for i in readables:
            if i == server:  # 如果是服务器就执行accept
                client, _ = i.accept()
                r_list.append(client)
            else:  # 是一个客户端 那就recv收数据
                try:
                    data = i.recv(1024)
                    if not data:  # linux对方强行下线或者是windows正常下线
                        i.close()
                        r_list.remove(i)
                        continue
                    print(data)
                    #     发送数据 不清楚 目前是不是可以发 所以交给select来检测
                    w_list.append(i)
                    data_dic[i] = data  # 把要发送的数据先存着 等select告诉你这个这个连接可以发送时再发送
                except ConnectionResetError:  # windows强行下线
                    i.close()
                    r_list.remove(i)  # 从检测列表中删除
        # 发送数据
        for i in writeables:
            try:
                i.send(data_dic[i].upper())  # 返回数据
                # data_dic.pop(i)
                # w_list.remove(i)
            except ConnectionResetError:
                i.close()
                # data_dic.pop(i)
                # w_list.remove(i)
            finally:
                data_dic.pop(i)  # 删除已经发送成功的数据
                w_list.remove(i)  # 从检测列表中删除这个连接 如果不删除 将一直处于可写状态
    server
  • 相关阅读:
    @PathVariable和@RequestParam的区别,@SessionAttributes
    forward和redirect的区别
    JSP页面的静态包含和动态包含
    ConcurrentHashMap源码解析
    Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX
    最小生成树
    tomcat启动项目内存溢出问题
    强引用,弱引用,4种Java引用浅解(涉及jvm垃圾回收)
    CXF 在WAS上报Unmarshalling Error的问题
    CXF处理Date类型的俩种方式
  • 原文地址:https://www.cnblogs.com/ShenJunHui6/p/10518735.html
Copyright © 2020-2023  润新知