• 协程:gevent模块,遇到i/o自动切换任务 038


    协程 : gevent模块,遇到io自动切换任务

    from gevent import monkey;monkey.patch_all()  # 写在最上面 这样后面的所有阻塞就全部能够识别了
    import gevent  # 直接导入即可
    from threading import current_thread
    import time
    def eat(name):
        print('%seat1'% name)
        # gevent.sleep(2)
        time.sleep(2)   # 加上monkey就能
        print('%seat2'% name)
        print(current_thread().getName())
    def play(name):
        print('%splay1'% name)
        # gevent.sleep(2)
        time.sleep(2)
        print('%splay2'% name)
        print(current_thread().getName())
    g1 = gevent.spawn(eat,'egon')  # 创建一个协程对象 spawn括号内第一个参数是函数名
    g2 = gevent.spawn(play,'egon')
    gevent.joinall([g1,g2]) # 将g1.join()和g2.join()合成一步写了出来
    print('')
    # 结果为
    # egoneat1
    # egonplay1
    # egoneat2
    # DummyThread-1  # 假线程 虚拟线程 其实都在一个线程里面 
    # egonplay2
    # DummyThread-2
    #

        gevent.sleep(2)模拟的是gevent可以识别的io阻塞,

        而 time.sleep(2)或其他阻塞 是不能直接识别的 需要用下面一行代码打补丁 就可以识别了

        from gevent import monkey;monkey.patch_all() 放在time, socket之前 或者直接将其放在文件开头.

        协程是通过自己的程序实现切换 自己能够控制 只有遇到协程模块能够识别io操作的时候, 程序才会进行任务切换 实现并发效果 , 如果所有的程序都没有io操作 那么久基本属于串行执行了.

    给大家看一个线程下(协程)来实现多个客户聊天

    io多路复用:模型(解决问题方案)

    #服务端
    from socket import *
    import select
    server  = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1',8093))
    server.listen(5)
    # 设置为非阻塞
    server.setblocking(False)
    # 将初始化服务端socket对象加入监听列表 后面还要动态添加一些conn连接对象, 当accept的时候sk就有感应 , 当recv的时候conn就有动静
    rlist = [server,]
    rdata = {}  # 存放客户端发送过来的消息
    wlist = []  # 等待写对象
    wdata = {}  # 存放要返回客户端的消息
    print('准备监听!')
    count = 0 # 写着计数用的 为了看实验效果用的 , 没用
    while 1:
        # 开始select监听,对rlist中服务端server进行监听 , select函数阻塞进程 , 直到rlist中套接字被触发(在此例中,套接字接收到客户端发来的握手信号 , 从而变得可读 满足select函数的可读条件),被触发的(有动静的)套接字(服务器套接字)返回给了rl这个返回值里面;
        rl,wl,xl = select.select(rlist,wlist,[],0.5)
        print('%s 次数>>'%(count),wl)
        count = count + 1
        # 对rl进行循环判断是否有客户端连接进来 , 当有客户端连接进来时select将触发
        for sock in rl:
            # 判断当前触发的是不是socket对象, 当触发的对象是socket对象时说明有新客户端accept连接进来了
            if sock == server:
                # 接收客户端的连接, 获取客户端对象和客户端地址信息
                conn,addr = sock.accept()
                # 把新的客户端连接加入到监听列表中 当客户端连接有接受消息的时候 , select将会被触发 ,会知道这个链接有动静 , 有消息 , 那么返回geirl这个返回值列表里面
                rlist.append(conn)
            else:
                # 由于客户端连接进来时socket接收客户端请求 , 将客户端连接加入到了监听列表中(rlist),客户端发送消息的时候这个链接将触发
                # 所以判断是否是客户端连接对象触发
                try:
                    data = sock.recv(1024)
                    # 没有数据的时候,将这个连接关闭掉 , 并从监听列表中移除
                    if not data:
                        sock.close()
                        rlist.remove(sock)
                        continue
                    print('received{0} from client{1}'.format(data.decode(),sock))
                    # 将接收到的客户端的消息保存下来
                    rdata[sock] = data.decode()
                    # 将客户端连接对象和这个对象接受到的消息加工成返回消息 , 并添加到wdata这个字典里面
                    wdata[sock] = data.upper()
                    # 要给这个客户端回复消息的时候, 我们将这个链接添加到wlist监听列表中
                    wlist.append(sock)
                # 如果这个连接出错了,客户端暴力断开了(注意 , 还没有接收到客户端的消息 , 或者接收消息的过程中出错了)
                except Exception:
                    # 关闭这个连接
                    sock.close()
                    # 在监听列表中将他删除 , 因为不管什么原因 毕竟是断开了 没必要再监听他了
                    rlist.remove(sock)
        # 如果现在没有客户端请求连接 也没有客户端发送消息时 , 开始对发送消息的列表进行处理 , 是否需要发送消息
        for sock in wl:
            sock.send(wdata[sock])
            wlist.remove(sock)
            wdata.pop(sock)
        # 将以此select监听列表中有接收数据的conn对象所接收到的消息打印一下
        # for k , v in rdata.items():
        #       print(k,'发来的消息是',v)
        # # 清空接收到的消息
        #rdata.clear()
    View Code

      知识总结 : 

        1 cs架构 bs架构

        2 网络通信流程:

           网卡 mac地址 子网掩码 网关 dns服务器 dhcp nat(网络地址转换)  端口(表示电脑上某个应用程序)  路由器 交换机 集线器 广播 单播 广播风暴 ARP协议 路由协议

        3 网络网络通信协议(**)

          osi七层 应表会传网数物

          tcpip协议  应用层 网络层 数据链路层 物理层

          tcp(*****)

            三次握手 客-->服--->客

            四次挥手 

            tcp和udp区别(*****)

              tcp: 面向连接 消息可靠 效率相对差 面向流的消息格式 无消息保护边界

              udp:面向无连接 不可靠 效率很高 面向包的消息格式 有消息保护边界

        4 socket

            缓冲区:

            粘包现象 : 1 连续发送小包并且间隔时间很短 就会发送两个消息合并在一起的情况 (Nagel优化算法导致的)  2 一次发送数据过大 对方一次未接收完 下次接受的时候连同第一次剩下的消息一同接受了 导致粘包

            解决粘包方案 都不知道对方发送的消息长度   1 发送消息之前先发送消息长度 收到对方确认信息后再次发送消息  2 通过struct 模块 自定义报头 将消息长度打包成4个字节长度的信息 连同你要发送的数据一并发过去

            打包 pack('i',长度)   长度是个整数       

  • 相关阅读:
    HDU1548:A strange lift
    hdu1978_How many ways dfs+记忆化搜索
    HDU1518:Square(DFS)
    HDU ACM 1495 非常可乐(广搜BFS)
    HDU1372:Knight Moves(BFS)
    662_二叉树最大宽度
    590_N叉树的后序遍历
    一直在坚持
    动态链接库DLL的操作顺序
    最近木马的查杀方法/各类杀毒软件的使用
  • 原文地址:https://www.cnblogs.com/f-g-f/p/9879942.html
Copyright © 2020-2023  润新知