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)
     

     

  • 相关阅读:
    Flex 布局教程:语法篇(转载)
    【Go】【Http】Go实现Http相关知识点
    【Git】Git相关开发流程
    【Go】杂七杂八GoLang
    【Go】初识Context与Context键值对的可能情况
    jmeter-通过json提取器 提取所有数据 给下个接口使用
    C# 后台调用存储过程超时处理方法,
    IE11脚本错误-调用的对象无效-
    IE11浏览器arrt,全选反选失效无效修改方法
    如何学习计算机知识
  • 原文地址:https://www.cnblogs.com/wangkun122/p/7994487.html
Copyright © 2011-2022 走看看