7.24 IO多路复用和协程代码笔记

1. 复习

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/23 11:49
# !@Author   TrueNewBee
# 1.班级 姓名 作业的内容
# ftp
#
# 笔记


# 今天的内容
#   协程
#   网络IO模型

2.协程

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/23 11:52
# !@Author   TrueNewBee
# 进程    启动多个进程 进程之间是由操作系统负责调用
# 线程    启动多个线程 真正被CPU执行的最小单位实际是线程
#         开启一个线程 创建一个线程 寄存器 堆栈
#         关闭一个线程
# 协程
#         本质上是一个线程
#         能够在多个任务之间切换来节省一些IO时间
# 实现并发的手段


# def consumer():
#     """创建一个生成器"""
#     while True:
#         x = yield
#         print('处理了数据', x)
#
#
# def producer():
#     c = consumer()
#     next(c)
#     for i in range(10):
#         print('生产了数据:', i)
#         c.send(i)
#
#
# producer()


# 真正的协程模块就是使用greenlet完成的切换
# from greenlet import greenlet
# # 不知道这个模块为何报错
# # 2018-7-23 12:34:39 吃饭去
#
#
# def eat():
#     print('eating start')
#     g2.switch()     # 切换到play
#     print('eating end')
#     g2.switch()
#
#
# def play():
#     print('playing start ')
#     g1.switch()
#     print('playing end')
#
#
# if __name__ == '__main__':
#     # 用于切换线程
#     g1 = greenlet(eat)
#     g2 = greenlet(play)
#     g1.switch()

# 放在开头,是为了识别time (IO)
# from gevent import monkey; monkey.patch_all()
# import time
# import gevent
# import threading
#
#
# def eat():
#     print(threading.current_thread())   # 查看线程名字
#     print('eating start')
#     time.sleep(1)   # gevent 检测到停1s,则调到另外一个函数中
#     print('eating end')
#
#
# def play():
#     print(threading.current_thread())
#     print('playing start ')
#     time.sleep(1)
#     print('playing end')
#
#
# if __name__ == '__main__':
#     g1 = gevent.spawn(eat)  # 开启协程
#     g2 = gevent.spawn(play)
#     g1.join()
#     g2.join()

# 进程和线程的任务切换由操作系统完成
# 协程任务之间的切换由程序(代码)完成 只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换实现并发效果

# 同步 和 异步 (网络操作常用协程)
# from gevent import monkey; monkey.patch_all()
# import time
# import gevent
#
#
# def task():
#     time.sleep(1)
#     print(12345)
#
#
# def sync():
#     for i in range(10):
#         task()
#
#
# def async():
#     g_list = []
#     for i in range(10):
#         g = gevent.spawn(task)
#         g_list.append(g)
#     gevent.joinall(g_list)  # for g in g_list :g.join()
#
#
# if __name__ == '__main__':
#     sync()
#     async()

# 协程 : 能够在一个线程中实现并发效果的概念
#        能够规避一些任务中的IO操作
#        在任务的执行过程中,检测到IO就切换到其他任务

# 多线程  被弱化了
# 协程: 在一个线程上,提高cpu的利用率
# 协程相比于多线程的优势  切换的效率更快了


# 爬虫例子(正则基础)
# 请求过程中的IO等待
from gevent import monkey;monkey.patch_all()
import gevent
from urllib.request import urlopen


def get_url(url1):
    response = urlopen(url1)
    content = response.read().decode('utf-8')   # 有各式的
    return len(content)


url = {
    'http://www.baidu.com',
    'http://www.taobao.com',
    'http://www.hao123.com',
}
g_list = []
for i in url:
    g = gevent.spawn(get_url, i)
    g_list.append(g)
gevent.joinall(g_list)
for g in g_list:
    print(g.value)
# socket server

3.用协程写 socket_demo

socket 

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/23 16:40
# !@Author   TrueNewBee
# 用协程写 socket
# 用协程是最快最方便的  最省时间占用最小,代码间的转换
from gevent import monkey; monkey.patch_all()
import socket
import gevent


def talk(conn1):
    conn1.send(b'hello')
    rec = conn.recv(1024).decode('utf-8')
    print(rec)
    conn.close()


if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    while True:
        conn, add = sk.accept()
        gevent.spawn(talk, conn)
    sk.close()

client

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/23 16:40
# !@Author   TrueNewBee
import socket


sk = socket.socket()
sk.connect(('127.0.0.1', 8080))
rec = sk.recv(1024).decode('utf-8')
print(rec)
msg = input('>>>>').encode('utf-8')
sk.send(msg)
sk.close()

4.IO模型 笔记

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/24 8:58
# !@Author   TrueNewBee
# 同步 : 提交一个任务之后要等待这个任务执行完毕
# 异步 : 只管提交任务,不等待这个任务执行完毕就可以做其他事情
# 阻塞 : input  urlopen()  在socket里面:recv() recvfrom() accept
# 非阻塞 : 除了阻塞的其他都是非阻塞

# 阻塞  线程  运行状态 --> 阻塞状态-->就绪
# 非阻塞

# IO多路复用
#   select机制   Windows和linux            都是操作系统轮询每一个被监听的项,看是否读操作
#   poll机制      linux   它可以监听的对象比select机制可以监听的多
#                           随着监听项的增多,导致效率降低
#   epoll机制     linux

5.非阻塞模型

socket

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/24 9:18
# !@Author   TrueNewBee
# 非阻塞IO模型
# 单线程中非阻塞!(没有用协程!)
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.setblocking(False)   # 默认True阻塞, False非阻塞
sk.listen()
conn_list = []
del_conn = []   # 存入失效连接的列表
while True:
    # 接收异常 BlockingIOError 完成非阻塞
    try:
        conn, add = sk.accept()     # 不阻塞,但没人连我会报错
        print('建立连接了', add)
        # msg = conn.recv(1024)       # 不阻塞,但没有消息会报错
        # print(msg)
        conn_list.append(conn)
    except BlockingIOError:
        # 循环列表连接 看看是否有人发消息
        for con in conn_list:
            try:
                msg = con.recv(1024)    # 不阻塞,但没有消息会报错
                if msg == b'':
                    del_conn.append(con)    # 把失效的连接存到del_conn中
                    continue
                print(msg)
                con.send(b'bye bye')
            except BlockingIOError:
                pass
        for con in del_conn:
            con.close()
            conn_list.remove(con)   # 在conn_list中删除失效连接
        del_conn.clear()    # 清空删除列表

5. client

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/24 9:18
# !@Author   TrueNewBee
# 非阻塞IO 多线程并发socket IO
import time
import socket
import threading


def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    sk.send(b'hello')
    time.sleep(1)
    msg = sk.recv(1024)
    print(msg)
    sk.close()


for i in range(20):
    threading .Thread(target=func).start()

6.IO多路复用 

socket

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/24 11:51
# !@Author   TrueNewBee
# IO多路复用 多并发!
import select
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.setblocking(False)
sk.listen()

read_list = [sk]  # 储存监听对象
while True:  # [sk, conn]  sk,发送链接    conn监听发送消息
    r_list, w_list, x_list = select.select(read_list, [], [])
    for i in r_list:
        if i is sk:
            conn, add = i.accept()  # 没有sk, 有conn则会报错
            read_list.append(conn)
        else:
            ret = i.recv(1024)
            if ret == b'':
                i.close()
                read_list.remove(i)
                continue
            print(ret)
            i.send(b'goodbye')

client

# !/usr/bin/env python
# !--*--coding:utf-8 --*--
# !@Time    :2018/7/24 11:51
# !@Author   TrueNewBee
import socket
import threading
import time


def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    sk.send(b'hello')
    time.sleep(1)
    sk.recv(1024)
    sk.close()


for i in range(20):
    threading .Thread(target=func).start()

7.selector_dome

 1 # !/usr/bin/env python
 2 # !--*--coding:utf-8 --*--
 3 # !@Time    :2018/7/24 17:15
 4 # !@Author   TrueNewBee
 5 # 服务端
 6 from socket import *
 7 import selectors
 8 sel = selectors.DefaultSelector()
 9 
10 
11 def accept(server_fileobj, mask):
12     conn, addr = server_fileobj.accept()
13     sel.register(conn, selectors.EVENT_READ, read)
14 
15 
16 def read(conn, mask):
17     try:
18         data = conn.recv(1024)
19         if not data:
20             print('closing', conn)
21             sel.unregister(conn)
22             conn.close()
23             return
24         conn.send(data.upper()+b'_SB')
25     except Exception:
26         print('closing', conn)
27         sel.unregister(conn)
28         conn.close()
29 
30 
31 sk = socket(AF_INET, SOCK_STREAM)
32 sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
33 sk.bind(('127.0.0.1', 8088))
34 sk.listen(5)
35 sk.setblocking(False)  # 设置socket的接口为非阻塞
36 # 相当于网select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
37 sel.register(sk, selectors.EVENT_READ, accept)
38 # 说白了就是,如果有人请求连接sk,就调用accept方法
39 
40 while True:
41     events = sel.select()  # 检测所有的sk,conn,是否有完成wait data的
42     for sel_obj, mask in events:    # [sk]
43         callback = sel_obj.data  # callback = accept
44         callback(sel_obj.fileobjmask)  # accept(server_fileobj,1)
45 
46 # #客户端
47 # from socket import *
48 # c=socket(AF_INET, SOCK_STREAM)
49 # c.connect(('127.0.0.1',8088))
50 #
51 # while True:
52 #     msg=input('>>: ')
53 #     if not msg:continue
54 #     c.send(msg.encode('utf-8'))
55 #     data = c.recv(1024)
56 #     print(data.decode('utf-8'))
57 #
58 # 基于selectors模块实现聊天

 

posted @ 2018-07-24 18:10  我想喝杨枝甘露~  阅读(176)  评论(0编辑  收藏  举报