Python(网络编程、并发编程)

目录

一、术语扫盲

二、网络编程

1、套接字通信socket

1.1、基于tcp协议

1.2、基于udp协议

2、粘包问题

3、套接字实现并发socketserver

3.1、基于tcp协议

3.2、基于udp协议

4、linux安装python

三、并发编程 

1、操作系统发展史

2、多道技术(单核实现并发的效果)

四、进程

1、进程理论

2、开启进程的两种方式

3、进程对象的方法

4、进程间数据相互隔离(通过第三方模块交互)

4.1、队列

4.2、IPC机制

4.3、生产者消费者模型

5、守护进程

五、线程

1、线程理论

2、开启线程的两种方式

3、线程对象的方法

4、同一进程内的多个线程数据共享

5、守护线程

六、锁

1、互斥锁

2、GIL全局解释器锁

3、死锁与递归锁

4、信号量

七、拓展

1、Event事件

2、多进程与多线程比较

3、进程池与线程池

4、协程

5、IO模型


一、术语扫盲

1、CS架构与BS架构
CS即Client/Server(客户机/服务器)结构
BS即Browser/Server(浏览器/服务器)结构
    开发维护成本:cs开发维护成本高于bs
    安全性:cs安全性高于bs
    客户端负载:cs客户端负载大于bs
    响应速度:cs相应速度高于bs

2、网络通信
网络=物理链接介质+互联网通信协议
网络存在的意义就是跨地域数据传输===>称之为通信

3、OSI七层协议
五层协议
        应用层
        传输层
        网络层
        数据链路层
        物理层
协议:规定数据的组织格式(头部+数据部分)

3.1物理层
一组数据称为:位(电信号010101)
单纯的电信号毫无意义,必须对其进行分组

3.2数据链路层:ethernet以太网协议
一组数据称为:帧=头(源mac地址与目标mac地址)+数据(网络层发过来的整体内容)
mac地址:但凡接入互联网的主机必须有一块网卡,每块网卡在出厂时都烧制好全世界独一无二的地址
计算机通信基本靠吼,即以太网协议的工作方式是广播

3.3网络层:IP协议
一组数据称为:包=头(源IP地址与目标IP地址)+数据(传输层发过来的整体内容)
要达到的目的:
    划分广播域
    每一个广播域但凡要接通外部,一定要有一个网关帮内部的计算机转发包到网上
    网关与外界通信走的是路由协议
一个合法的ipv4地址=ip地址/子网掩码地址

3.4传输层:tcp\udp协议
一组数据称为:段=头(源port与目标port)+数据(应用层发过来的整体内容)
基于端口,端口范围0-65535,0-1023为系统占用端口
ip+port ===> 标识全世界范围内独一无二的一个基于网络通信的应用程序
基于tcp协议通信之前:必须建立一个双向通信的链接
三次握手,四次挥手
tcp是可靠传输的,发送数据必须等到对方确认后才算完成,才会将自己内存中的数据清除
ps:当服务端大量处于TIME_WAIT状态时意味着服务端正在经历高并发

3.5应用层
可以自定义协议=》头部(对数据的描述信息发给谁数据的类型长度等)+数据部分
http https ftp



总结:
mac => 标识局域网内独一无二的一台计算机
ip + mac => 标识全世界范围内独一无二的一台计算机
ARP协议通过ip地址解析mac地址:
    两台计算机在同一局域网内(源ip与目标ip网络地址一样),目标mac地址就是计算机2的mac地址
    两台计算机不在同一局域网内(源ip与目标ip网络地址不一样),目标mac地址就是网关的mac地址
ip  => 标识全世界范围内独一无二的一台计算机
ip + port => 标识全世界范围内独一无二的一台计算机上的一个基于网络通信的应用程序

应用层                          数据
传输层                      tcp
网络层                  ip
数据链路层     ethernet
物理层
(原mac地址,目标mac地址) (源ip地址,目标ip地址) (源port,目标port) 数据
在经由物理层转换为电信号

二、网络编程

1、套接字通信socket

1.1、基于tcp协议

import socket

server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8080))
server.listen(5)

# 链接循环
while True:
    conn,addr=server.accept()
    # 通信循环
    while True:
        try:
            data=conn.recv(1024)
            conn.send(data.upper())
        except Exception:
            break
    conn.close()
import socket

client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

client.connect(("127.0.0.1",8080))
while True:
    msg=input("请输入信息:").strip()
    if msg=="":continue
    client.send(msg.encode("utf-8"))
    data=client.recv(1024)
    print(data.decode("utf-8"))
client.close()

1.2、基于udp协议

import socket

server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(("127.0.0.1",8080))

while True:
    data,addr=server.recvfrom(1024)
    server.sendto(data.upper(),addr)
import socket

server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg=input("请输入:").strip()
    server.sendto(msg.encode("utf-8"),("127.0.0.1",8080))
    res=server.recvfrom(1024)
    print(res)

2、粘包问题

import socket
import os
import json
import struct

server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8080))
server.listen(5)

conn,addr=server.accept()
while True:
    try:
        path=conn.recv(1024)
        path=path.decode("utf-8")
        filename=os.path.basename(path)
        size=os.path.getsize(filename)

        # 制作头:包含数据的一些信息
        header={
            "filename":filename,
            "size":size
        }
        header_str=json.dumps(header)
        header_bytes=header_str.encode("utf-8")
        # 先发送头的长度:4个字节
        x=struct.pack("i", len(header_bytes))
        conn.send(x)
        # 再发送头
        conn.send(header_bytes)
        # 再发送真实数据
        with open(path,"rb") as f:
            while True:
                msg = f.read(1024)
                if len(msg)==0:
                    break
                conn.send(msg)

    except Exception:
        break
conn.close()
'''
粘包问题出现的原因
1、tcp是流式协议,数据像水流一样粘在一起没有边界区分
2、收数据没有收干净,残留部分会和下一次结果混在一起
ps:
1、udp协议是报式协议,数据一报一报的一个recv对应一个send
2、收不干净就丢掉,所以没有粘包问题

解决粘包问题的思路:每次都收干净就行
1、拿到数据的总大小total_size
2、recv_size+=循环接收的数据,直至recv_size=total_size
'''
import socket
import struct
import json

client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

client.connect(("127.0.0.1",8080))
while True:
    msg=input("请输入文件路径:").strip()
    if msg=="":continue
    client.send(msg.encode("utf-8"))
    # 先接收4个字节,获取头的长度
    x=client.recv(4)
    header_len=struct.unpack("i",x)[0]
    # 根据头的长度,再接收头
    header_bytes=client.recv(header_len)
    header_str=header_bytes.decode("utf-8")
    header=json.loads(header_str)
    # 根据头的内容,再接收真实数据
    total_size=header["size"]
    recv_size=0
    while recv_size<total_size:
        msg=client.recv(1024)
        try:
            print(msg.decode("utf-8"),end="")
        except Exception:
            pass
        recv_size+=len(msg)
    else:
        print()
client.close()

3、套接字实现并发socketserver

3.1、基于tcp协议

import socketserver

class MyRequestHandler(socketserver.BaseRequestHandler):
    # 通信循环:启动一个线程进行通信循环
    def handle(self):
        # self.request => 链接对象
        # self.client_address => 客户端ip、port
        while True:
            try:
                data = self.request.recv(1024)
                self.request.send(data.upper())
            except Exception:
                break
        self.request.close()

# 链接循环:从半链接池中取出链接请求,与其建立双向链接
s=socketserver.ThreadingTCPServer(("127.0.0.1",8080),MyRequestHandler)
s.serve_forever()
import socket

client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

client.connect(("127.0.0.1",8080))
while True:
    msg=input("请输入信息:").strip()
    if msg=="":continue
    client.send(msg.encode("utf-8"))
    data=client.recv(1024)
    print(data.decode("utf-8"))
client.close()

3.2、基于udp协议

import socketserver

class MyRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # self.request => (b'hello', <socket.socket fd=360, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
        # self.client_address => ('127.0.0.1', 52491)
        client_data=self.request[0]
        server=self.request[1]
        client_addr=self.client_address
        server.sendto(client_data.upper(),client_addr)

s=socketserver.ThreadingUDPServer(("127.0.0.1",8080),MyRequestHandler)
s.serve_forever()
import socket

server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg=input("请输入:").strip()
    server.sendto(msg.encode("utf-8"),("127.0.0.1",8080))
    res=server.recvfrom(1024)
    print(res)

4、linux安装python

cd /home
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tgz
tar -xvf Python-3.7.6.tgz
cd Python-3.7.6
./configure --prefix=/usr/local/python3
make && make install
vim /etc/profile

三、并发编程 

1、操作系统发展史

参考博客:操作系统的发展史 - JasonJi - 博客园 (cnblogs.com)

2、多道技术(单核实现并发的效果)

并发与并行

  • 并发:看起来像同时运行的
  • 并行:真正意义上的同时执行

如何实现

  • 空间上的复用:多个程序共用一套计算机硬件
  • 时间上的复用:切换(一个程序遇到IO操作、长时间占用cpu)+保存状态

多道技术图解

四、进程

1、进程理论

程序与进程

  • 程序:躺在硬盘的一堆代码,是静态的。(程序想要被计算机运行,它的代码必须要先由硬盘读到内存,之后CPU取指令再执行)
  • 进程:程序的执行过程,是动态的。(创建进程就是在内存中申请一块空间,将需要运行的代码丢进去,一个进程对应一块独立的内存空间)

进程的调度

  • 先来先服务调度算法:对长作业有利,对短作业无利
  • 短作业优先调度算法:对短作业有利,对长作业无利
  • 时间片轮转法+多级反馈队列:

同步与异步(描述的是任务的提交方式)

  • 同步:任务提交后,原地等待任务的返回结果
  • 异步:任务提交之后,不原地等待任务的返回结果,任务的返回结果会有一个异步回调机制自动处理

阻塞与非阻塞 (描述的是程序的运行状态)

  • 阻塞:阻塞态
  • 非阻塞:就绪态、运行态

时间片轮转法+多级反馈队列

进程运行的三状态图

2、开启进程的两种方式

from multiprocessing import Process
import time

def task(name):
    print("{} is running".format(name))
    time.sleep(3)
    print("{} is over".format(name))

if __name__ == '__main__':
    # 1、创建一个进程
    p=Process(target=task,args=("Cang",))
    # 2、开启进程
    p.start()
    print("主")
from multiprocessing import Process
import time

class MyProcess(Process):
    def run(self):
        print("aaa")
        time.sleep(3)
        print("bbb")

if __name__ == '__main__':
    p=MyProcess()
    p.start()
    print("主")

3、进程对象的方法

from multiprocessing import Process,current_process
import os
import time

def func():
    # print("进程:",current_process().pid)
    print("子进程:",os.getpid())        # 查看当前进程的pid
    print("主进程:",os.getppid())       # 查看当前进程的父进程的pid

if __name__ == '__main__':
    p=Process(target=func)
    p.start()
    # p.terminate()            # 杀死当前进程,是告诉操作系统帮你杀死进程,但是需要一定的时间
    # print(p.is_alive())      # 判断当前进程是否存活
    p.join()                   # 等待子进程运行结束之后,再继续运行主进程,不影响其它子进程的运行

4、进程间数据相互隔离(通过第三方模块交互)

4.1、队列

import queue
q=queue.Queue(3)          # 括号内可以传数字,标示生成的队列最大可以同时存放的数据量
q.put(111)
q.put(222)
q.put(333)
# q.put(444)              # 当队列为满时,等待存值
# print(q.empty())        # 判断队列是否为空
# print(q.full())         # 判断队列是否为满
v1=q.get()
v2=q.get()
v3=q.get()
# v4=q.get()              # 当队列为空时,等待取值
# v4=q.get(timeout=3)     # 只等3s,若还没有值报错queue.Empty
# v4=q.get_nowait()       # 等都不等,没值直接报错queue.Empty


import queue
q=queue.LifoQueue(3)
q.put(111)
q.put(222)
q.put(333)
print(q.get())
print(q.get())
print(q.get())


import queue
q=queue.PriorityQueue(3)
q.put((3,111))
q.put((5,222))
q.put((-1,333))
print(q.get())
print(q.get())
print(q.get())

4.2、IPC机制

from multiprocessing import Process, Queue

def producer(q):
    q.put(111)

def consumer(q):
    print(q.get())

if __name__ == '__main__':
    q=Queue()
    p1=Process(target=producer,args=(q,))
    p2=Process(target=consumer,args=(q,))
    p1.start()
    p2.start()

4.3、生产者消费者模型

from multiprocessing import Process,JoinableQueue
import time
import random

def producer(name,food,q):
    for i in range(3):
        data="{}生产{}{}".format(name,food,i)
        print(data)
        time.sleep(random.random())
        q.put(data)

def consumer(name,q):
    while True:
        data=q.get()
        time.sleep(random.random())
        print("{}吃{}".format(name,data))
        q.task_done()

if __name__ == '__main__':
    q=JoinableQueue()
    p1=Process(target=producer,args=("egon","包子",q))
    p2=Process(target=producer,args=("tank","泔水",q))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    c1=Process(target=consumer,args=("cang",q))
    c1.daemon=True
    c1.start()
    q.join()
    print("zhu")

5、守护进程

from multiprocessing import Process
import time

def task(name):
    print("{} is running".format(name))
    time.sleep(3)
    print("{} is over".format(name))

if __name__ == '__main__':
    p=Process(target=task,args=("cang",))
    p.daemon=True       # 将进程p设置成守护进程,这一句一定要放在start方法上面才有效否则会报错
    p.start()
    print("主寿终正寝")

五、线程

1、线程理论

进程:资源单位(创建一个进程就是在内存中申请一块空间将需要运行的代码丢进去)
线程:执行单位(线程指得是代码的执行过程,执行过程中需要的资源向线程所在的进程索要)

为何要有线程:开设线程的开销要远远小于开设进程的开销(申请内存空间、拷贝代码)

2、开启线程的两种方式

from threading import Thread
import time

def task(name):
    print("{} is running".format(name))
    time.sleep(1)
    print("{} is over".format(name))

if __name__ == '__main__':
    t=Thread(target=task,args=("cang",))
    t.start()
    print("zhu")
from threading import Thread
import time

class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        print("{} is running".format(self.name))
        time.sleep(1)
        print("{} is over".format(self.name))

if __name__ == '__main__':
    t=MyThread("cang")
    t.start()
    print("zhu")

3、线程对象的方法

from threading import Thread,current_thread,active_count
import time
import os

def task(n):
    print(os.getpid())
    print(current_thread().name)
    time.sleep(n)

if __name__ == '__main__':
    t1=Thread(target=task,args=(1,))
    t2=Thread(target=task,args=(2,))
    t1.start()
    t2.start()
    t1.join()                       # 等待子线程运行结束后再运行主线程,不影响其它子线程的运行
    print(os.getpid())              # 获取进程的pid
    print(current_thread().name)    # 获取线程的名字
    print(active_count())           # 统计当前正在活跃的线程数

4、同一进程内的多个线程数据共享

from threading import Thread

money=100

def task():
    global money
    money=666

if __name__ == '__main__':
    t=Thread(target=task)
    t.start()
    t.join()
    print(money)

5、守护线程

from threading import Thread
import time

def task():
    print("123")
    time.sleep(1)
    print("end123")

def foo():
    print("456")
    time.sleep(3)
    print("end456")

if __name__ == '__main__':
    t1=Thread(target=task)
    t2=Thread(target=foo)
    t1.daemon=True
    t1.start()
    t2.start()
    print("zhu")

六、锁

1、互斥锁

'''
互斥锁:
    多个进程/线程操作同一份数据的时候,会出现数据错乱的问题
    加锁处理,将并发变成串行,牺牲效率但是保证了数据的安全性
'''
from multiprocessing import Process,Lock
import json
import time
import random

def search(i):
    with open("a.txt",mode="rt",encoding="utf-8") as f:
        res=f.read()
        dic=json.loads(res)
    print("用户{}查询票数:{}".format(i,dic.get("ticket_num")))

def buy(i):
    with open("a.txt",mode="rt",encoding="utf-8") as f:
        res=f.read()
        dic=json.loads(res)
    time.sleep(random.randint(1,3))
    if dic.get("ticket_num")>0:
        dic["ticket_num"] -= 1
        with open("a.txt",mode="wt",encoding="utf-8") as f:
            res=json.dumps(dic)
            f.write(res)
        print("用户{}买票成功".format(i))
    else:
        print("用户{}买票失败".format(i))

def run(i,mutex):
    search(i)
    # 给买票环节加锁处理
    # 枪锁
    mutex.acquire()
    buy(i)
    # 释放锁
    mutex.release()

if __name__ == '__main__':
    # 在主进程中生成一把锁,让所有的子进程抢,谁先抢到谁买票
    mutex=Lock()
    for i in range(1,10):
        p=Process(target=run,args=(i,mutex))
        p.start()

2、GIL全局解释器锁

'''
GIL全局解释器锁:
    是Cpython解释器的特点,而不是python的特点
    保证的是解释器级别的数据安全,针对不同的数据还是需要加不同的锁来处理
    会导致同一个进程下的多个线程无法同时执行,也就无法利用多核优势(解释型语言的通病)
'''
from threading import Thread, Lock
import time

money = 100

def task(mutex):
    global money
    mutex.acquire()            
    tem = money
    time.sleep(0.01)
    money = tem - 1
    mutex.release()

if __name__ == '__main__':
    mutex = Lock()
    t_list = []
    for i in range(100):
        t = Thread(target=task, args=(mutex,))
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(money)

'''
100个线程起起来之后,去抢GIL
我抢到了GIL,又抢到了互斥锁(无人争抢,其它线程还在等着抢GIL)
我进入IO 自动释放GIL,其它线程虽然抢到了GIL但是抢不到互斥锁,最后还是乖乖释放GIL
我又抢到了GIL,释放互斥锁,释放GIL
'''

3、死锁与递归锁

'''
死锁:
    我手上有锁A,然后去抢锁B
    你手上有锁B,然后去抢锁A

递归锁:
    可以被连续的acquire和release
    每acquire一次计数加一,每release一次计数减一
    只要计数不为0,那么其他人都无法抢到该锁
'''
from threading import Thread, RLock
import time

mutexB = mutexA = RLock()

class MyThread(Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        self.task1()
        self.task2()

    def task1(self):
        mutexA.acquire()
        print("线程{}抢到锁A".format(self.name))
        mutexB.acquire()
        print("线程{}抢到锁B".format(self.name))
        mutexB.release()
        print("线程{}释放锁B".format(self.name))
        mutexA.release()
        print("线程{}释放锁A".format(self.name))

    def task2(self):
        mutexB.acquire()
        print("线程{}抢到锁B".format(self.name))
        time.sleep(1)
        mutexA.acquire()
        print("线程{}抢到锁A".format(self.name))
        mutexA.release()
        print("线程{}释放锁A".format(self.name))
        mutexB.release()
        print("线程{}释放锁B".format(self.name))

if __name__ == '__main__':
    for i in range(10):
        t = MyThread(i)
        t.start()

4、信号量

'''
信号量:
    在不同的阶段可能对应不同的技术点,在并发编程中指的是锁
    如果将互斥锁比成一个坑的话,那么信号量就相当于多个坑
'''
from threading import Thread, Semaphore
import time
import random

sp = Semaphore(5)  # 坑位

def task(name):
    sp.acquire()
    print("线程{}在蹲坑".format(name))
    time.sleep(random.randint(1,3))
    sp.release()

if __name__ == '__main__':
    for i in range(20):
        t=Thread(target=task,args=(i,))
        t.start()

七、拓展

1、Event事件

'''
Event事件:
    一些进程/线程需要等待另外一些进程/线程运行完毕之后才能运行,类似发射信号
'''
from threading import Thread, Event
import time

sub=Event()

def light():
    print("红灯亮")
    time.sleep(3)
    print("绿灯亮")
    sub.set()

def car(name):
    print("{}等待绿灯".format(name))
    sub.wait()
    print("{}加油门飙起来".format(name))

if __name__ == '__main__':
    t=Thread(target=light)
    t.start()
    for i in range(20):
        t=Thread(target=car,args=(i,))
        t.start()

2、多进程与多线程比较

'''
同一个进程下的多个线程无法利用多核优势,那么多线程是不是没有用了

计算密集型(多核四个任务)
    多进程:总耗时10+
    多线程:总耗时40+
IO密集型(多核四个任务)
    多进程:相对浪费资源
    多线程:相对节省资源

总结:多进程和多线程都有各自的优势,通常使用多进程下开设多线程,这样既可以利用多核也可以减少资源消耗
'''
from multiprocessing import Process
from threading import Thread
import time
import os

def task():
    # m = 1
    # for i in range(1, 100000):
    #     m *= i
    time.sleep(0.01)

if __name__ == '__main__':
    start_time=time.time()
    print(os.cpu_count())
    p_list = []
    for i in range(10):
        # p = Process(target=task)
        # p.start()
        # p_list.append(p)
        t=Thread(target=task)
        t.start()
        p_list.append(t)
    for p in p_list:
        p.join()
    print(time.time()-start_time)

3、进程池与线程池

'''
进程池与线程池:
    在保证计算机安全的情况下,最大限度的利用计算机
    虽然降低了效率,但是它保证了计算机的安全
'''
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time

def task(n):
    print(n)
    time.sleep(2)
    return "===>:{}".format(n ** 2)

def call_back(res):
    print(res.result())

if __name__ == '__main__':
    pool = ThreadPoolExecutor(5)
    for i in range(20):
        res = pool.submit(task, i)
        res.add_done_callback(call_back)

4、协程

'''
进程:资源单位
线程:执行单位
协程:单线程实现并发
    本来cpu遇到IO或者长时间被占用,需要进行切换
    但是我们程序员自己在代码层面上检测所以的IO操作,一旦遇到IO了我们在代码级别完成切换
    这样给cpu的感觉就是没有遇到IO,从而提高了程序的运行效率
'''
# import time
# def task1():
#     for i in range(10000000):
#         i+1
# def task2():
#     for i in range(10000000):
#         i+1
#
# start_time=time.time()
# task1()
# task2()
# print(time.time()-start_time)   # 1.5311427116394043

# import time
# def task1():
#     for i in range(10000000):
#         i + 1
#         yield
# def task2():
#     t = task1()
#     for i in range(10000000):
#         i + 1
#         next(t)
#
# start_time = time.time()
# task2()
# print(time.time() - start_time)     # 2.8123199939727783
# 总结:计算密集型任务,切换会降低效率

# import time
# 
# def task1():
#     print("hello")
#     time.sleep(2)
# def task2():
#     print("world")
#     time.sleep(3)
# start_time = time.time()
# task1()
# task2()
# print(time.time() - start_time)     # 5.0310328006744385

from gevent import monkey;monkey.patch_all()
from gevent import spawn
import time

def task1():
    print("hello")
    time.sleep(2)
def task2():
    print("world")
    time.sleep(3)
start_time = time.time()
s1=spawn(task1)
s2=spawn(task2)
s1.join()
s2.join()
print(time.time() - start_time)     # 3.031080722808838

5、IO模型

5.1、阻塞模型

import socket

server=socket.socket()
server.bind(("127.0.0.1",8080))
server.listen(5)

while True:
    conn,addr=server.accept()
    while True:
        try:
            data=conn.recv(1024)
            conn.send(data.upper())
        except Exception:
            break
    conn.close()
import socket

client=socket.socket()

client.connect(("127.0.0.1",8080))
while True:
    client.send("hello".encode("utf-8"))
    data=client.recv(1024)
    print(data.decode("utf-8"))

5.2、非阻塞模型

'''
虽然非阻塞IO模型给你的感觉很牛逼
但是该模型会长时间占用cpu而不干活
'''

import socket

server=socket.socket()
server.bind(("127.0.0.1",8080))
server.listen(5)
server.setblocking(False)

r_list=[]
del_list=[]
while True:
    try:
        conn,addr=server.accept()
        r_list.append(conn)
    except BlockingIOError:
        for conn in r_list:
            try:
                data = conn.recv(1024)
                conn.send(data.upper())
            except BlockingIOError:
                continue
            except ConnectionRefusedError:
                conn.close()
                del_list.append(conn)
                continue
        for conn in del_list:
            r_list.remove(conn)
        del_list.clear()
import socket

client=socket.socket()

client.connect(("127.0.0.1",8080))
while True:
    client.send("hello".encode("utf-8"))
    data=client.recv(1024)
    print(data.decode("utf-8"))

5.3、IO多路复用

'''
select机制    windows、linux都有
poll机制      只有linux有
    select和poll都可以监管多个对象,但是poll监管的数量更多
    上述两个机制对于监管特别多的对象时,会出现相当大的延时响应
            
epoll机制     只有linux有
    它给每个对象都绑定了一个回调机制,一旦有响应回调机制立刻发起提醒

针对不同的操作系统还要考虑不同的监管机制,书写代码太过繁琐
这是有一个人跳出来了(selectors模块)能够根据你跑的平台不同,自动帮你选择对应的监管机制
'''
import socket
import select

server=socket.socket()
server.bind(("127.0.0.1",8080))
server.listen(5)
server.setblocking(False)

read_list = [server]            # 将server对象添加到监管列表中
while True:
    r_list,w_list,x_list=select.select(read_list,[],[])     # 操作系统会监管这些对象,返回可执行的对象
    for i in r_list:                        # 针对不同的对象作不同的操作
        if i is server:                     # 针对可执行的server对象,建立链接,并将该链接对象conn添加到监管列表中
            conn,addr=i.accept()
            read_list.append(conn)
        else:                               # 针对可执行的conn对象,进行接收数据操作
            try:
                data=i.recv(1024)
                conn.send(data.upper())
            except Exception:
                i.close()
                read_list.remove(i)
import socket

client=socket.socket()

client.connect(("127.0.0.1",8080))
while True:
    client.send("hello".encode("utf-8"))
    data=client.recv(1024)
    print(data.decode("utf-8"))

5.4、异步IO

异步IO模型是所有模型中效率最高的,也是应用最广泛的

相关的模块与框架

        模块:asyncio

        框架:sanic        tronado        twisted

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值