一、自定义的异步非阻塞的客户端

#!/usr/bin/env python
# -*- coding: utf8 -*-
# __Author: "Skiler Hao"
# date: 2017/5/16 15:04
import select
import socket
import pprint """
自定义了异步IO模块
利用非阻塞的socket,不等待连接是否成功,不等待请求的响应
select模块,去监听创建的套接字,是否有准备写,准备读的 """ class HttpResponse:
def __init__(self, response_data):
self.raw_data = response_data
self.data = str(response_data, encoding='utf-8')
self.Header = {}
self.GET = {}
self.BODY = {}
self.response_info = ''
self.initialize() def initialize(self):
header_data, body_data = self.data.split('\r\n\r\n', 1)
self.BODY = body_data header_list = header_data.split('\r\n')
# print(header_list)
for header_item in header_list:
header_byte = header_item.split(':', 1)
if len(header_byte) == 2:
self.Header[header_byte[0]] = header_byte[1] else:
self.response_info = header_byte def __str__(self):
return self.response_info class HttpRequest:
"""
对HttpRequest的简单封装,将socket,host和callback封装成一个对象
""" def __init__(self, sk, host, callback):
"""
收到socket,host, callback回调函数,将其封装成HttRequest对象
:param sk:一个socket对象
:param host:主机名或者域名
:param callback:socket结束后的回调函数
"""
self.socket = sk
self.host = host
self.callback = callback def fileno(self):
"""
select可以直接监听
定义一个fileno()并返回一个,可以将HttpRequest()对象直接放在select直接去轮训,监听
:return:fileno()文件描述符
"""
return self.socket.fileno() class AsyncHttpClient:
"""
异步Http客户端
""" def __init__(self):
self.conn = []
self.connection = [] def add_request(self, host, callback):
"""
传过来一个host和callback,自动将其封装HttpRequest对象,然后将其加入到self.conn和self.connection中
:param host: 传过来一个host,一个回调函数callback
:param callback:
:return:
"""
sk = socket.socket()
sk.setblocking(0) # 创建一个非阻塞的socket
try:
sk.connect((host, 80)) # 这个socket去连指定的主机的80端口
except BlockingIOError as e:
pass
# 将socket,host,callback封装成一个对象
http_request = HttpRequest(sk, host, callback) # 将对象追加到conn和connection
self.conn.append(http_request)
self.connection.append(http_request) def run(self):
# 任务调用的接口
while True:
# 监听套接字的信号
# select(rlist,wlist,xlist)
# 三个列表,select分别监听 三个列表,rlist是否有读信号,wlist是否写信号,xlist是否有异常信号,最后是timeout
# 一旦有某个对象相应的信号,就将当前的当前的对象返回给对应的位置
rlist, wlist, xlist = select.select(self.conn, self.connection, self.conn, 0.05)
# 一旦有写信号
for w in wlist:
# 首次就是服务器,告知连接成功,你可以往里面写数据啦
# print(w.host,'连接成功,赶紧写数据吧!')
# 在套接字赶紧写上我要获取你的首页
tpl = "GET / HTTP/1.0\r\nHost:%s\r\n\r\n" % (w.host,)
w.socket.send(bytes(tpl, encoding='utf-8'))
# select就不用监听这个套接字是否有写入信号,直接移除掉
self.connection.remove(w) # 一旦有读信号
for r in rlist:
# print(r.host,'开始收到数据啦~~~') rec_data = bytes()
while True:
# 开始收数据,直到接收不到数据
try:
chunk = r.socket.recv(8096) # 一次接受8096个字节的数据
rec_data += chunk
except BlockingIOError as e:
break
# print(r.host,rec_data) # 执行打包中的callback方法,将接收到数据传过去
r.callback(rec_data)
r.socket.close()
# 获取到相应的相应数据,就不在监听其是否有读数据
self.conn.remove(r)
if len(self.conn) == 0:
# 如果没有要监听是否有读信号的套接字,就可以跳出循环任务结束了
break def f1(rec_data):
response = HttpResponse(rec_data)
print(response.BODY) def f2(data):
print('输出到屏幕') # 定义一个字典列表,字典列表包含主机名和回调函数
url_list = [
{'host': 'www.baidu.com', 'callback': f1},
{'host': 'cn.bing.com', 'callback': f1},
{'host': 'www.cnblogs.com', 'callback': f1},
]
# 声明一个异步Async requestclient = AsyncHttpClient() for item in url_list:
requestclient.add_request(item['host'], item['callback']) requestclient.run()

二、自定义的异步非阻塞的服务端

#!/usr/bin/env python
# -*- coding: utf8 -*-
# __Author: "Skiler Hao"
# date: 2017/5/19 7:05
import socket, select EOL1 = b'/r/n'
EOL2 = b'/r/n/r/n' # 拼接成的response
response = b'HTTP/1.0 200 OK/r/nDate: Mon, 1 Jan 1996 01:01:01 GMT/r/n'
response += b'Content-Type: text/plain/r/nContent-Length: 13/r/n/r/n'
response += b'Hello, world!' # 创建一个服务端的socket,来监听是否有请求过来
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080)) # 绑定
serversocket.listen(1) # 监听
serversocket.setblocking(0) # 设置时非阻塞
print(serversocket.fileno())
epoll = select.epoll() # 准备设计一个IO多路复用
epoll.register(serversocket.fileno(), select.EPOLLIN) # 把上面的serversocket的fileno() 和 监听准备读信号 注册到epoll中去 try:
# 设置三个空字典
connections = {}
requests = {}
responses = {}
while True:
# 查看epoll是否有信号有的话,就放在events中
events = epoll.poll(1) # 循环events,分别拿到 文件描述号 和对应的事件
for fileno, event in events:
# 如果当前的文件描述号是serversocket,那么说明有新的连接
if fileno == serversocket.fileno():
# 所以就得接受,创建了 连接,拿到了对方的IP地址
connection, address = serversocket.accept()
# connection就是客户端连接过来建立的socket,设置为非阻塞
connection.setblocking(0)
# 客户端建立的socket也注册到select模块的IO多路复用中去
epoll.register(connection.fileno(), select.EPOLLIN) # 以Connection的文件描述号 作为键 socket作为值保存在connections中
connections[connection.fileno()] = connection # 同时在requests和responses字典中,
# requests中 以connection.fileno() 作为键 以请求的内容作为值
# responses中 以connection.fileno() 作为键 以相应的内容作为值,这个我们返回的是固定的,仅仅返回hello world requests[connection.fileno()] = b''
responses[connection.fileno()] = response # 如果请求的数据不是socketserver,那肯定是客户端的,判断是否是准备读的信号
elif event & select.EPOLLIN:
# 立马来开始读取数据,加到requests对象套接字的内容中去
requests[fileno] += connections[fileno].recv(1024) # 判断换行 和 两个换行是否在接收过来的数据中
if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
# 如果是的话,就将这个套接字的监听更新为准备写
epoll.modify(fileno, select.EPOLLOUT) # 打印40个-,然后换行,加上请求的内容
print('-' * 40 + '/n' + requests[fileno].decode()[:-2])
# 如果请求的数据不是socketserver,那肯定是客户端的,判断是否是准备写的信号
elif event & select.EPOLLOUT:
# 立马来开始读取数据,就将response的对应的套接字号对应的值拿出来,其实就是hello world,O(∩_∩)O哈哈~
# 并统计发送了多少个字节
byteswritten = connections[fileno].send(responses[fileno])
# 更新response对应套接字内容为剩下的内容
responses[fileno] = responses[fileno][byteswritten:]
# 如果内容发完了,剩下的长度就是0,如果长度是0
if len(responses[fileno]) == 0:
# 就修改select,不监听该套接字的内容,其就变成了EPOLLHUP
epoll.modify(fileno, 0)
# 然后关闭该socket的管道
connections[fileno].shutdown(socket.SHUT_RDWR)
elif event & select.EPOLLHUP:
# 如果不监听该套接字的内容,就将其注销掉
epoll.unregister(fileno)
# 关闭该套接字
connections[fileno].close()
# 从连接中删除该文件描述符
del connections[fileno]
finally:
# 最后关闭serversocket服务器套接字
epoll.unregister(serversocket.fileno())
# 关闭epoll
epoll.close()
# 套接字关闭
serversocket.close()

Tornado之自定义异步非阻塞的服务器和客户端的更多相关文章

  1. 03: 自定义异步非阻塞tornado框架

    目录:Tornado其他篇 01: tornado基础篇 02: tornado进阶篇 03: 自定义异步非阻塞tornado框架 04: 打开tornado源码剖析处理过程 目录: 1.1 源码 1 ...

  2. 200行自定义异步非阻塞Web框架

    Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...

  3. Tornado异步非阻塞的使用以及原理

    Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...

  4. Tornado之异步非阻塞

    同步模式:同步模式下,只有处理完前一个任务下一个才会执行 class MainHandler(tornado.web.RequestHandler): def get(self): time.slee ...

  5. NIO:异步非阻塞I/O,AIO,BIO

    Neety的基础使用及说明 https://www.cnblogs.com/rrong/p/9712847.html BIO(缺乏弹性伸缩能力,并发量小,容易出现内存溢出,出现宕机 每一个客户端对应一 ...

  6. 使用tornado让你的请求异步非阻塞

    http://www.dongwm.com/archives/shi-yong-tornadorang-ni-de-qing-qiu-yi-bu-fei-zu-sai/?utm_source=tuic ...

  7. 异步非阻塞IO的Python Web框架--Tornado

    Tornado的全称是Torado Web Server,从名字上就可知它可用作Web服务器,但同时它也是一个Python Web的开发框架.最初是在FriendFeed公司的网站上使用,FaceBo ...

  8. Tornado的异步非阻塞

    阻塞和非阻塞Web框架 只有Tornado和Node.js是异步非阻塞的,其他所有的web框架都是阻塞式的. Tornado阻塞和非阻塞两种模式都支持. 阻塞式: 代表:Django.Flask.To ...

  9. Tornado 异步非阻塞

    1 装饰器 + Future 从而实现Tornado的异步非阻塞 class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine def ...

随机推荐

  1. no crontab for root 解决方案

    root用户下 输入 crontab -l 显示 no crontab for root  例如: [root@localhost ~]# crontab -l no crontab for root ...

  2. [Vue]组件——实现动态组件:keep-alive的使用

    1.在app.vue中用一个 <keep-alive> 元素将其动态组件包裹起来: keepAlive为true时,第一次被创建的时候缓存下来,为false时,不会缓存 <keep- ...

  3. LA-4255 Guess (拓扑排序+构造)

    题目大意:一个未知的整数序列,给出其任意一个区间和的正负,还原这个序列.任意一个满足条件的序列即可. 题目分析:将连续区间和转化为前缀和之差,sumx-1与sumy的大小关系已知,以此建立一条有向边, ...

  4. 11g OCM自动打补丁

    1.先替换掉OPatch软件 每个实例都要运行 GRID_HOME和ORACLE_HOME的OPatch目录都去除掉 把OPatch软件p6880880_112000_Linux-x86-64.zip ...

  5. 002——vue小结

    1.new 一个vue对象的时候你可以设置他的属性,其中最重要的包括三个,分别是:data,methods,watch. 2.其中data代表vue对象的数据,methods代表vue对象的方法,wa ...

  6. SQL语句往Oracle数据库中插入日期型数据(to_date的用法)

    Oracle 在操作数据库上相比于其他的 T-sql 有微小的差别,但是在插入时间类型的数据是必须要注意他的 to_date 方法,具体的情况如下: --SQL语句往Oracle数据库中插入日期型数据 ...

  7. Django WSGI,MVC,MTV,中间件部分,Form初识

    一.什么是WSGI? WEB框架的本质是一个socket服务端接收用户请求,加工数据返回给客户端(Django),但是Django没有自带socket需要使用 别人的 socket配合Django才能 ...

  8. iOS开发:MKMapView地图内存持续增加的释放解决办法

    内存持续增加的释放解决办法 最近修改一个用到MKMapView的项目,内存一直占用过多,每次拖拽地图时还会增加占用,且一直无法释放. 经过两天的排查,最后锁定是创建的self.map对象在加载地图的时 ...

  9. iOS使用Instruments的工具

    使用Instruments的工具 iOSXcodeInstrumentsInstruments是一个官方提供的强大的性能调试工具集. 1.Blank(空模板):创建一个空的模板,可以从Library库 ...

  10. 剑指offer--40.翻转单词顺序列

    时间限制:1秒 空间限制:32768K 热度指数:276854 本题知识点: 字符串 题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写 ...