zoukankan      html  css  js  c++  java
  • day36 python学习gevent io 多路复用 socketserver *****

    ---恢复内容开始---

    gevent

    1、切换+保存状态
    2、检测单线程下任务的IO,实现遇到IO自动切换

    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

    #用法
    g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数
    ,可以是位置实参或关键字实参,都是传给函数eat的 g2=gevent.spawn(func2) g1.join() #等待g1结束 g2.join() #等待g2结束 #或者上述两步合作一步:gevent.joinall([g1,g2]) g1.value#拿到func1的返回值

    遇到IO阻塞时会自动切换任务

    #初识gevent
    import gevent
    def task():
        print('aaa')
        gevent.sleep(3)#  当遇到io 时 不会等,会切到task1,然后执行task1的代码,然后又遇到IC再切回来
        #回来一看还在睡,然后再切到task1  来回切直到有一个睡醒了就行了
        print('cccc')
    def task1():
        print('aaa')
        gevent.sleep(2)
        print('cccc')
    
    t1=gevent.spawn(task,)
    t2=gevent.spawn(task1,)
    t1.join()
    t2.join()
    print('zhu')

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

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

    from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前

    或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头

    from gevent import monkey,spawn;monkey.patch_all()  #这个模块的引入一定要放在最上边
    #因为socket
    from socket import *
    import time
    #from threading import Thread
    def server(ip,cont):
        server=socket(AF_INET,SOCK_STREAM)
        server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
        server.bind((ip,cont))
        server.listen(5)
        while True:
            print('aaa')
            time.sleep(0.1)
            print('bbb')
            conn,addr=server.accept()#  因为接受的过程中会有IO
            # (即是accept 在等 wait data 和data copy这两个过程)
            #然后就会去切到task 函数 task 函数如果没有内容就会切回来来回切换
            #这样减少了等待,提高了效率,(相当与单线程下实现并发)
            spawn(task,conn,)      #  实现了单线程下的并发,因为这里是新开了一个函数
            # 而不是新开了一个线程或者进程,所以这样弄节省内存空间
    def task(conn):
        while True:
            print('ccc')
            try:
                data=conn.recv(1024)
                if not data:continue
                conn.send(data.upper())
            except ConnectionResetError:
                conn.close()
    
    if __name__ == '__main__':
        server('127.0.0.1',8090)
    #1 对cpu的占用率过多,但是是无用的占用
    #2 在连接数过多的情况下,不能及时响应客户的的消息
    from socket import *
    import time
    server=socket(AF_INET,SOCK_STREAM)
    server.bind(('127.0.0.1',8090))
    server.listen(5)
    server.setblocking(False)#设置为
    conn_l=[] #将conn 加到列表中,让下一列用
    while True:
        try:
            conn,addr=server.accept()
            conn_l.append(conn)
            print(addr)
        except BlockingIOError:
            print('去干其他活儿了',len(conn_l))
            del_l=[]
            for conn in conn_l: #在这里用例表中存入的内容
                try:
                    data=conn.recv(1024)
                    if not data:
                        del_l.append(data) #如果是空的放到问题项列表中,
                        continue
    
                    conn.send(data.upper())
                except BlockingIOError:
                    pass
                except ConnectionResetError:
                    conn.close()  #关闭conn
                    del_l.append(conn)
                for i in del_l:
                    conn_l.remove(i)  #删除有问题的内容。
    非阻塞IO模型

    socketserver模块

    import socketserver
    class MyTCPHandler(socketserver.BaseRequestHandler):#定义一个类继承他得使用他的规则
        def handle(self):  #处理通信的活儿
            print('=====>',self)#self.request==conn  他们两个是等价的意思
            print(self.request)  #打印的内容为<socket.socket fd=452, family=AddressFamily.AF_INET,
            #  type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080),
            # raddr=('127.0.0.1', 63347)> 他是套接字对象,等价于conn  包含recv方法
            while True:
                data=self.request.recv(1024)
                self.request.send(data.upper())
    
    
    
    
    if __name__ == '__main__':
        # socketserver.ForkingTCPServer linux的用这个
        server=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyTCPHandler)
        server.serve_forever()
    
         #以上的代码等价于如下的操作,建立连接,相当于把他封装在了这个对象中
        # while True:
        #     conn,addr=server.accept()
        #     t=Thread(target=func,args=(conn,))
        #     t.start()

     SocketServer 模块  重要*****

    SocketServer内部使用 IO多路复用 以及 “多线程”“多进程” ,从而实现并发处理多个客户端请求的Socket服务端

    #tcp 服务端
    import
    socketserver class MyTCPHandler(socketserver.BaseRequestHandler):#定义一个类继承他得使用他的规则 def handle(self): #处理通信的活儿 print('=====>',self)#self.request==conn 他们两个是等价的意思 print(self.request) #打印的内容为<socket.socket fd=452, family=AddressFamily.AF_INET, # type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), # raddr=('127.0.0.1', 63347)> 他是客户端的套接字对象,等价于conn # 包含recv方法 while True: data=self.request.recv(1024) self.request.send(data.upper()) if __name__ == '__main__': # socketserver.ForkingTCPServer linux的用这个 server=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyTCPHandler) #?? server.serve_forever() #循环的建链接, #以上的代码等价于如下的操作,建立连接,相当于把他封装在了这个对象中 # while True: # conn,addr=server.accept() # t=Thread(target=func,args=(conn,)) # t.start()
    #UDP服务端
    import
    socketserver class MyUDPhandler(socketserver.BaseRequestHandler): def handle(self): print(self.request)#(b'ss', <socket.socket fd=408, # family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, # proto=0, laddr=('127.0.0.1', 8080)>) #打印出来的是一个元祖, 第一个值是收到客户端发送的信号,第二个值是 #服务端---套接字对象 client_data=self.request[0] #cclient_data 这个格式是固定的,不要改变它 self.request[1].sendto(client_data.upper(),self.client_address)#client_address 这个是他自己有的 if __name__ == '__main__': server=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyUDPhandler) server.serve_forever()

    I/O多路复用
    Linux中的 select,poll,epoll 都是IO多路复用的机制。I/O多路复用指:通过一种机制,可以监视多个描述符

    一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

    #select 的功能只是监测套接字有没有数据,不负责取
    from socket import *
    import time
    import select
    server=socket(AF_INET,SOCK_STREAM)
    server.bind(('127.0.0.1',8090))
    server.listen(5)
    server.setblocking(False)#设置为
    read_l=[server,] #初始状态加server, 初始状态要对这个套接字对象进行监控,
    # 当不阻塞时(即有客户端连过来的)select
    while True:
        r1,w1,x1=select.select(read_l,[],[])##read_l=[server,conn1,conn2,conn3]
        #  里边要传入三个参数,
        # 当要用那个的时候传入对应的列表,要监控读时要在第一个
        # 不用的传入空列表
        #r1[]列表中只寸准备好的套接字
        #print(r1[0] is server)#  r1[0]是一个套接字对象  服务端
        for r in r1:#循环列表,将里边准备好的内容执行,实现并发的效果
            if r is server:  #判断好了的对象是 server 还是conn
                conn,addr=r[0].accept()
                print(addr)
                read_l.append(conn)
            else:            #如果是conn就执行这些代码
                try:
                    data=r.recv(1024)
                    if not data:
                        r.close()
                        read_l.remove(r)
                    r.send(data.upper())
                except ConnectionResetError:
                    r.close()
                    read_l.remove(r)
     

     

  • 相关阅读:
    欧拉回路 定理
    UESTC 1087 【二分查找】
    POJ 3159 【朴素的差分约束】
    ZOJ 1232 【灵活运用FLOYD】 【图DP】
    POJ 3013 【需要一点点思维...】【乘法分配率】
    POJ 2502 【思维是朴素的最短路 卡输入和建图】
    POJ 2240 【这题貌似可以直接FLOYD 屌丝用SPFA通过枚举找正权值环 顺便学了下map】
    POJ 1860【求解是否存在权值为正的环 屌丝做的第一道权值需要计算的题 想喊一声SPFA万岁】
    POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】
    js 实现slider封装
  • 原文地址:https://www.cnblogs.com/wangkun122/p/7994487.html
Copyright © 2011-2022 走看看