zoukankan      html  css  js  c++  java
  • socket服务端实现并发、进程池/线程池、协程、IO模型

    一.soccket服务端实现并发

    ​ 网络编程服务端要满足一下三点要求:

    ​ - 1. 固定的ip和port

    ​ - 2. 24小时不间断提供服务

    ​ - 3. 能够实现并发

    #服务端
    import socket
    from threading import Thread
    """
    服务端:
        1.固定的ip和port
        2.24小时不间断提供服务
        3.支持高并发
    """
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)  # 半连接池
    
    
    def communicate(conn):
        while True:
            try:
                data = conn.recv(1024)  # 阻塞
                if len(data) == 0:break
                print(data)
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    
    while True:
        conn,addr = server.accept()  # 阻塞
        print(addr)
        t = Thread(target=communicate,args=(conn,))
        t.start()
    
    # 客户端
    import socket
    
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    while True:
        info = input('>>>:').encode('utf-8')
        if len(info) == 0:continue
        client.send(info)
       data = client.recv(1024)
        print(data)	
    

    二.进程池和线程池

    ​ - 无论开线程还是开进程其实都消耗资源,开线程消耗的资源比开进程小

    ​ - 池: 为了减缓计算机硬件的压力,避免计算机硬件设备奔溃

    ​ 虽然减轻了计算机硬件的压力,但是一定程度上降低了持续的效率

    ​ - 进程池/线程池:

    ​ 为了限制开设的进程数和线程数,从而保证计算机硬件的安全

    from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
    import time
    import os
    
    # 示例化池对象
    # 不知道参数的情况,默认是当前计算机cpu个数乘以5,也可以指定线程个数
    pool = ProcessPoolExecutor(5)  # 创建了一个池子,池子里面有20个线程
    
    
    def task(n):
        print(n, os.getpid())
        time.sleep(2)
        return n ** 2
    
    
    def call_back(n):
        print('我拿到了结果:%s' % n.result())
    
    
    """
    提交任务的方式
        同步:提交任务之后,原地等待任务的返回结果,再继续执行下一步代码
        异步:提交任务之后,不等待任务的返回结果(通过回调函数拿到返回结果并处理),直接执行下一步操作
    """
    
    # 回调函数:异步提交之后一旦任务有返回结果,自动交给另外一个去执行
    if __name__ == '__main__':
        # pool.submit(task,1)
        t_list = []
        for i in range(20):
            future = pool.submit(task, i).add_done_callback(call_back)  # 异步提交任务
            t_list.append(future)
    
        # pool.shutdown()  # 关闭池子并且等待池子中所有的任务运行完毕
        # for p in t_list:
        #     print('>>>:',p.result())
        print('主')
    

    三.协程

    ​ - 进程:资源单位(车间)

    ​ - 线程:最小执行单位(流水线)

    ​ - 协程:单线程下实现并发

    ​ 1.协程完全是程序员自己想出来的的东西,通过代码层面自己检测io自己实现切换,

    ​ 让操作系统误认为这个线程没有io

    ​ 2.切换+保存状态一定可以提升程序效率吗?

    ​ 按情况考虑:1.当任务是计算密集型的任务时,反而会降低效率

    ​ 2.当任务是IO密集型任务是,可以提高运行效率

    ​ 3.实现高并发:将单线程的效率提升到最高,多进程先开多线程,多线程下使用协程

    3.1 gevent模块

    ​ 一个spawn就是一个管理任务的对象

    ​ gevent模块不能识别它本身之外的IO行为

    ​ 所以必须导入它内部封装的模块,可以帮助我们识别所有io行为

    from gevent import monkey;monkey.patch_all()  # 检测所有的IO行为
    from gevent import spawn,joinall  # joinall列表里面放多个对象,实现join效果
    import time
    
    def play(name):
        print('%s play 1' %name)
        time.sleep(5)
        print('%s play 2' %name)
    
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(3)
        print('%s eat 2' %name)
    
    
    start=time.time()
    g1=spawn(play,'刘清正')
    g2=spawn(eat,'刘清正')
    
    # g1.join()
    # g2.join()
    joinall([g1,g2])
    print('主',time.time()-start)  # 单线程下实现并发,提升效率
    

    3.2 运用协程实现服务端客户端通信

    链接和通信都是io密集型操作,我们要在这两者之间来回切换,就能实现并发效果

    服务端检测连接和通信任务,客户端起多线程同时链接服务器

    # 服务端
    from gevent import monkey;monkey.patch_all()
    from socket import *
    from gevent import spawn
    
    
    def communicate(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0: break
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
        
    
    def server(ip, port, backlog=5):
        server = socket(AF_INET, SOCK_STREAM)
        server.bind((ip, port))
        server.listen(backlog)
    
        while True:  # 链接循环
            conn, client_addr = server.accept()
            print(client_addr)
    
            # 通信
            spawn(comunicate,conn)
    
    
    if __name__ == '__main__':
        g1=spawn(server,'127.0.0.1',8080)
        g1.join()
    
        
    # 客户端
    from threading import Thread, current_thread
    from socket import *
    
    
    def client():
        client = socket(AF_INET, SOCK_STREAM)
        client.connect(('127.0.0.1', 8080))
    
        n = 0
        while True:
            msg = '%s say hello %s' % (current_thread().name, n)
            n += 1
            client.send(msg.encode('utf-8'))
            data = client.recv(1024)
            print(data.decode('utf-8'))
    
    
    if __name__ == '__main__':
        for i in range(500):
            t = Thread(target=client)
            t.start()
    # 原本服务端需要开启500个线程才能跟500个客户端通信,现在只需要一个线程就可以扛住500客户端
    # 进程下面开多个线程,线程下面再开多个协程,最大化提升软件运行效率
    

    四. io模型

    • 阻塞IO

    • 非阻塞IO(服务端通信针对accept用s.setblocking(False)加异常捕获,cpu占用率过高)

    • IO多路复用

      在只检测一个套接字的情况下,他的效率连阻塞IO都比不上。因为select这个中间人增加了环节。

      但是在检测多个套接字的情况下,就能省去wait for data过程

    • 异步IO

  • 相关阅读:
    Emote木马分析
    CentOS7安装部署MongoDB
    CentOS7搭建FastDFS文件管理服务器
    CentOS7搭建FTP服务器
    20179301《网络攻防实践》第九周作业
    20179301《网络攻防实践》第七周作业
    20179301 段晓庆 《网络攻防》第六周总结
    20179301 《网络攻防技术》第四周总结
    20179301 段晓庆 《网络攻防》第三周总结
    2017-2018-2 20179301《网络攻防技术》第一周作业
  • 原文地址:https://www.cnblogs.com/majingjie/p/10838675.html
Copyright © 2011-2022 走看看