自制web框架

1、核心IO多路复用部分

  1. # -*- coding:utf-8 -*-
  2. import socket
  3. import select
  4.  
  5. class Snow():
  6.  
  7. def __init__(self):
  8. self.inputs = set()
  9.  
  10. def run(self,ip="localhost",port=9999):
  11. sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  12. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  13. sock.bind((ip,port))
  14. sock.setblocking(False)
  15. sock.listen(128)
  16. self.inputs.add(sock)
  17.  
  18. while True:
  19. # 使用select模块达到io多路复用
  20. readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs, 0.005)
  21. for conn in readable_list:
  22. if sock is conn:
  23. # 新建连接
  24. client,address = conn.accept()
  25. client.setblocking(False)
  26. self.inputs.add(client)
  27. else:
  28. # 接收数据get/post
  29. recv_bytes = bytes("",encoding="utf-8")
  30. while True:
  31. try:
  32. _recv = conn.recv(8096)
  33. recv_bytes += _recv
  34. except:
  35. # 循环收齐数据后,由于set blocking(False),所以出发except
  36. break
  37. print(recv_bytes)
  38. conn.sendall(bytes("HTTP/1.1 200 OK\r\nContent-Length: {len}\r\n\r\n{body}".format(len=len("hello world"),body="hello world"),encoding="utf-8"))
  39. self.inputs.remove(conn)
  40.  
  41. core/main.py

core/main.py

  1. # -*- coding:utf-8 -*-
  2.  
  3. from core.main import Snow
  4.  
  5. if __name__ == '__main__':
  6. print("http://127.0.0.1:9999")
  7. Snow().run()

app.py

2、封装Request/Response类

  1. # -*- coding:utf-8 -*-
  2. import socket
  3. import select
  4.  
  5. class HttpRequest():
  6. def __init__(self, conn):
  7. self.conn = conn
  8. self.headers_bytes = bytes("", encoding="utf-8")
  9. self.body_bytes = bytes("", encoding="utf-8")
  10. self.initial()
  11.  
  12. def initial(self):
  13. # 接收数据get/post
  14. split_flag = False
  15. while True:
  16. try:
  17. _recv_bytes = self.conn.recv(8096)
  18. except:
  19. # 循环收齐数据后,由于setblocking(False),所以触发except
  20. break
  21.  
  22. if split_flag:
  23. self.body_bytes += _recv_bytes
  24. else:
  25. self.headers_bytes += _recv_bytes
  26. if b"\r\n\r\n" in self.headers_bytes:
  27. self.headers_bytes, self.body_bytes = self.headers_bytes.split(b"\r\n\r\n", 1)
  28. split_flag = True
  29.  
  30. class HttpResponse():
  31. def __init__(self, content=""):
  32. self.content = content
  33.  
  34. def response(self):
  35. return bytes(self.content,encoding="utf-8")
  36.  
  37. class Snow():
  38.  
  39. def __init__(self):
  40. self.inputs = set()
  41.  
  42. def run(self, ip="localhost", port=9999):
  43. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  44. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  45. sock.bind((ip, port))
  46. sock.setblocking(False)
  47. sock.listen(128)
  48. self.inputs.add(sock)
  49.  
  50. while True:
  51. # 使用select模块达到io多路复用
  52. readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs, 0.005)
  53. for conn in readable_list:
  54. if sock is conn:
  55. # 新建连接
  56. client, address = conn.accept()
  57. client.setblocking(False)
  58. self.inputs.add(client)
  59. else:
  60. # 把“接收数据get/post”这个封装到request里
  61. response = self.process(conn)
  62. conn.sendall(response.response())
  63. self.inputs.remove(conn)
  64.  
  65. def process(self,conn):
  66. request = HttpRequest(conn)
  67. res = HttpResponse("HTTP/1.1 200 OK\r\nContent-Length: {len}\r\n\r\n{body}".format(len=len("hello world"), body="hello world"))
  68. return res

core/main.py

  1. # -*- coding:utf-8 -*-
  2.  
  3. from core.main import Snow
  4.  
  5. if __name__ == '__main__':
  6. print("http://127.0.0.1:9999")
  7. Snow().run()

app.py不变

3、增加用户端router

  1. # -*- coding:utf-8 -*-
  2. import socket
  3. import select
  4. import re
  5.  
  6. class HttpRequest():
  7. def __init__(self, conn):
  8. self.conn = conn
  9. self.headers_bytes = bytes("", encoding="utf-8")
  10. self.body_bytes = bytes("", encoding="utf-8")
  11. self.initial()
  12.  
  13. def initial(self):
  14. # 接收数据get/post
  15. split_flag = False
  16. while True:
  17. try:
  18. _recv_bytes = self.conn.recv(8096)
  19. except:
  20. # 循环收齐数据后,由于setblocking(False),所以触发except
  21. break
  22.  
  23. if split_flag:
  24. self.body_bytes += _recv_bytes
  25. else:
  26. self.headers_bytes += _recv_bytes
  27. if b"\r\n\r\n" in self.headers_bytes:
  28. self.headers_bytes, self.body_bytes = self.headers_bytes.split(b"\r\n\r\n", 1)
  29. split_flag = True
  30.  
  31. class HttpResponse():
  32. def __init__(self, content=""):
  33. self.content = content
  34. self.template = "HTTP/1.1 200 OK\r\nContent-Length: {len}\r\n\r\n{body}"
  35.  
  36. def response(self):
  37. return bytes(self.template.format(
  38. len=len(self.content),
  39. body=self.content,
  40. ),encoding="utf-8")
  41.  
  42. class Snow():
  43.  
  44. def __init__(self,router):
  45. self.router = router
  46. self.inputs = set()
  47.  
  48. def run(self, ip="localhost", port=9999):
  49. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  50. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  51. sock.bind((ip, port))
  52. sock.setblocking(False)
  53. sock.listen(128)
  54. self.inputs.add(sock)
  55.  
  56. while True:
  57. # 使用select模块达到io多路复用
  58. readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs, 0.005)
  59. for conn in readable_list:
  60. if sock is conn:
  61. # 新建连接
  62. client, address = conn.accept()
  63. client.setblocking(False)
  64. self.inputs.add(client)
  65. else:
  66. # 把“接收数据get/post”这个封装到request里
  67. response = self.process(conn)
  68. if isinstance(response,HttpResponse):
  69. conn.sendall(response.response())
  70. self.inputs.remove(conn)
  71.  
  72. def process(self,conn):
  73. request = HttpRequest(conn)
  74. header_line1 = str(request.headers_bytes.split(b"\r\n",1)[0],encoding="utf-8")
  75. method,url,version = header_line1.split()
  76. func = None
  77. for kv in self.router:
  78. if len(kv) == 2:
  79. re_url = kv[0]
  80. if re.match(re_url,url):
  81. func = kv[1]
  82. break
  83. if func:
  84. return func()

core/main.py

  1. # -*- coding:utf-8 -*-
  2.  
  3. from core.main import Snow,HttpResponse
  4.  
  5. def index():
  6. return HttpResponse("index ok")
  7.  
  8. router = [
  9. (r"/index/",index),
  10. ]
  11.  
  12. if __name__ == '__main__':
  13. print("http://127.0.0.1:9999")
  14. Snow(router).run()

app.py

4、增加httpnotfound页面

  1. # -*- coding:utf-8 -*-
  2. import socket
  3. import select
  4. import re
  5.  
  6. class HttpRequest():
  7. def __init__(self, conn):
  8. self.conn = conn
  9. self.headers_dict = dict()
  10. self.headers_bytes = bytes("", encoding="utf-8")
  11. self.body_bytes = bytes("", encoding="utf-8")
  12. self.method = ""
  13. self.url = ""
  14. self.version = ""
  15.  
  16. self.initial()
  17. self.initial_headers()
  18.  
  19. @property
  20. def headers_str(self):
  21. return str(self.headers_bytes, encoding="utf-8")
  22.  
  23. def initial(self):
  24. # 接收数据get/post
  25. split_flag = False
  26. while True:
  27. try:
  28. _recv_bytes = self.conn.recv(8096)
  29. except:
  30. # 循环收齐数据后,由于setblocking(False),所以触发except
  31. break
  32.  
  33. if split_flag:
  34. self.body_bytes += _recv_bytes
  35. else:
  36. self.headers_bytes += _recv_bytes
  37. if b"\r\n\r\n" in self.headers_bytes:
  38. self.headers_bytes, self.body_bytes = self.headers_bytes.split(b"\r\n\r\n", 1)
  39. split_flag = True
  40.  
  41. def initial_headers(self):
  42. header_lines = self.headers_str.split("\r\n")
  43. first_line = header_lines[0].split()
  44. if len(first_line) == 3:
  45. self.method, self.url, self.version = first_line
  46.  
  47. for header_line in header_lines:
  48. kv = header_line.split(":", 1)
  49. if len(kv) == 2:
  50. k, v = kv
  51. self.headers_dict[k] = v
  52.  
  53. class HttpResponse():
  54. def __init__(self, content=""):
  55. self.content = content
  56. self.template = "HTTP/1.1 200 OK\r\nContent-Length: {len}\r\n\r\n{body}"
  57.  
  58. def response(self):
  59. return bytes(self.template.format(
  60. len=len(self.content),
  61. body=self.content,
  62. ), encoding="utf-8")
  63.  
  64. class HttpNotFound(HttpResponse):
  65.  
  66. def __init__(self):
  67. super(HttpNotFound, self).__init__('404 Not Found')
  68.  
  69. class Snow():
  70.  
  71. def __init__(self, router):
  72. self.router = router
  73. self.inputs = set()
  74. self.request = None
  75.  
  76. def run(self, ip="localhost", port=9999):
  77. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  78. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  79. sock.bind((ip, port))
  80. sock.setblocking(False)
  81. sock.listen(128)
  82. self.inputs.add(sock)
  83.  
  84. try:
  85. while True:
  86. # 使用select模块达到io多路复用
  87. readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs, 0.005)
  88. for conn in readable_list:
  89. if sock is conn:
  90. # 新建连接
  91. client, address = conn.accept()
  92. client.setblocking(False)
  93. self.inputs.add(client)
  94. else:
  95. # 把“接收数据get/post”这个封装到request里
  96. _process = self.process(conn)
  97. if isinstance(_process, HttpResponse):
  98. conn.sendall(_process.response())
  99. self.inputs.remove(conn)
  100. conn.close()
  101. else:
  102. # 可以做其他操作
  103. pass
  104.  
  105. except Exception as e:
  106. pass
  107. finally:
  108. sock.close()
  109.  
  110. def process(self, conn):
  111. self.request = HttpRequest(conn)
  112. func = None
  113. for route in self.router:
  114. if len(route) == 2:
  115. if re.match(route[0], self.request.url):
  116. func = route[1]
  117. break
  118. if func:
  119. return func(self.request)
  120. else:
  121. return HttpNotFound()

core/main.py

  1. # -*- coding:utf-8 -*-
  2.  
  3. from core.main import Snow,HttpResponse
  4.  
  5. def index(request):
  6. print(request.url)
  7. print(request.headers_bytes)
  8. print(request.body_bytes)
  9. return HttpResponse("index ok")
  10.  
  11. router = [
  12. (r"/index/",index),
  13. ]
  14.  
  15. if __name__ == '__main__':
  16. print("http://127.0.0.1:9999")
  17. Snow(router).run()

app.py

5、增加future对象

  1. # -*- coding:utf-8 -*-
  2. import socket
  3. import select
  4. import re
  5. import time
  6.  
  7. class HttpRequest():
  8. def __init__(self, conn):
  9. self.conn = conn
  10. self.headers_dict = dict()
  11. self.headers_bytes = bytes("", encoding="utf-8")
  12. self.body_bytes = bytes("", encoding="utf-8")
  13. self.method = ""
  14. self.url = ""
  15. self.version = ""
  16.  
  17. self.initial()
  18. self.initial_headers()
  19.  
  20. @property
  21. def headers_str(self):
  22. return str(self.headers_bytes, encoding="utf-8")
  23.  
  24. def initial(self):
  25. # 接收数据get/post
  26. split_flag = False
  27. while True:
  28. try:
  29. _recv_bytes = self.conn.recv(8096)
  30. except:
  31. # 循环收齐数据后,由于setblocking(False),所以触发except
  32. break
  33.  
  34. if split_flag:
  35. self.body_bytes += _recv_bytes
  36. else:
  37. self.headers_bytes += _recv_bytes
  38. if b"\r\n\r\n" in self.headers_bytes:
  39. self.headers_bytes, self.body_bytes = self.headers_bytes.split(b"\r\n\r\n", 1)
  40. split_flag = True
  41.  
  42. def initial_headers(self):
  43. header_lines = self.headers_str.split("\r\n")
  44. first_line = header_lines[0].split()
  45. if len(first_line) == 3:
  46. self.method, self.url, self.version = first_line
  47.  
  48. for header_line in header_lines:
  49. kv = header_line.split(":", 1)
  50. if len(kv) == 2:
  51. k, v = kv
  52. self.headers_dict[k] = v
  53.  
  54. class HttpResponse():
  55. def __init__(self, content=""):
  56. self.content = content
  57. self.template = "HTTP/1.1 200 OK\r\nContent-Length: {len}\r\n\r\n{body}"
  58.  
  59. def response(self):
  60. return bytes(self.template.format(
  61. len=len(self.content),
  62. body=self.content,
  63. ), encoding="utf-8")
  64.  
  65. class HttpNotFound(HttpResponse):
  66.  
  67. def __init__(self):
  68. super(HttpNotFound, self).__init__('404 Not Found')
  69.  
  70. class Future(object):
  71. """
  72. 异步非阻塞模式时封装回调函数以及是否准备就绪
  73. """
  74. def __init__(self, callback):
  75. self.callback = callback
  76. self._ready = False
  77. self.value = None
  78.  
  79. def set_result(self, value=None):
  80. self.value = value
  81. self._ready = True
  82.  
  83. @property
  84. def ready(self):
  85. return self._ready
  86.  
  87. class TimeoutFuture(Future):
  88. """
  89. 异步非阻塞超时
  90. """
  91. def __init__(self, timeout):
  92. super(TimeoutFuture, self).__init__(callback=None)
  93. self.timeout = timeout
  94. self.start_time = time.time()
  95.  
  96. @property
  97. def ready(self):
  98. current_time = time.time()
  99. if current_time > self.start_time + self.timeout:
  100. self._ready = True
  101. return self._ready
  102.  
  103. class Snow():
  104.  
  105. def __init__(self, router):
  106. self.router = router
  107. self.inputs = set()
  108. self.request = None
  109. self.async_request_handler = dict()
  110.  
  111. def run(self, ip="localhost", port=9999):
  112. print("http://{}:{}".format(ip,port))
  113. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  114. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  115. sock.bind((ip, port))
  116. sock.setblocking(False)
  117. sock.listen(128)
  118. self.inputs.add(sock)
  119.  
  120. try:
  121. while True:
  122. # 使用select模块达到io多路复用
  123. readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs, 0.005)
  124. for conn in readable_list:
  125. if conn is sock:
  126. # 新建连接
  127. client, address = conn.accept()
  128. client.setblocking(False)
  129. self.inputs.add(client)
  130. else:
  131. # 把“接收数据get/post”这个封装到request里
  132. _process = self.process(conn)
  133. if isinstance(_process, HttpResponse):
  134. conn.sendall(_process.response())
  135. self.inputs.remove(conn)
  136. conn.close()
  137. else:
  138. print(_process)
  139. # 可以做其他操作
  140. self.async_request_handler[conn] = _process
  141. self.polling_callback()
  142.  
  143. except Exception as e:
  144. print(e)
  145. pass
  146. finally:
  147. sock.close()
  148.  
  149. def polling_callback(self):
  150. for conn in list(self.async_request_handler.keys()):
  151. fut = self.async_request_handler[conn]
  152. if not fut.ready:
  153. continue
  154. if fut.callback:
  155. ret = fut.callback(self.request,fut)
  156. conn.sendall(ret.response())
  157. self.inputs.remove(conn)
  158. del self.async_request_handler[conn]
  159. conn.close()
  160.  
  161. def process(self, conn):
  162. self.request = HttpRequest(conn)
  163. func = None
  164. for route in self.router:
  165. if len(route) == 2:
  166. if re.match(route[0], self.request.url):
  167. func = route[1]
  168. break
  169. if func:
  170. return func(self.request)
  171. else:
  172. return HttpNotFound()

core/main.py

  1. # -*- coding:utf-8 -*-
  2.  
  3. from core.main import Snow,HttpResponse,TimeoutFuture,Future
  4.  
  5. def index(request):
  6. print(request.url)
  7. print(request.headers_bytes)
  8. print(request.body_bytes)
  9. return HttpResponse("index ok")
  10.  
  11. def async(request):
  12.  
  13. obj = TimeoutFuture(5)
  14.  
  15. return obj
  16.  
  17. request_list = []
  18.  
  19. def callback(request, future):
  20. return HttpResponse(future.value)
  21.  
  22. def req(request):
  23. obj = Future(callback=callback)
  24.  
  25. request_list.append(obj)
  26.  
  27. return obj
  28.  
  29. def stop(request):
  30. obj = request_list[0]
  31.  
  32. del request_list[0]
  33.  
  34. obj.set_result('done')
  35.  
  36. return HttpResponse('stop')
  37.  
  38. router = [
  39. (r"/index/",index),
  40. (r"/async/",async),
  41. (r'/req/', req),
  42. (r'/stop/', stop),
  43. ]
  44.  
  45. if __name__ == '__main__':
  46.  
  47. Snow(router).run()

app.py

超时

自定制stop

参考

http://www.cnblogs.com/wupeiqi/p/6536518.html

开发简单的IO多路复用web框架的更多相关文章

  1. python运维开发(十七)----jQuery续(示例)web框架django

    内容目录: jQuery示例 前端插件 web框架 Django框架 jQuery示例 dom事件绑定,dom绑定在form表单提交按钮地方都会绑定一个onclick事件,所有查看网站的人都能看到代码 ...

  2. python 全栈开发,Day66(web应用,http协议简介,web框架)

    一.web应用 web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件.应用程序有两种模式C/S.B/S.C/S是客户端 ...

  3. 第六模块:WEB框架开发 第1章·Django框架开发1~50

    01-Django基础介绍 02-Web应用程序1 03-Web应用程序2 04-http请求协议1 05-http请求协议2 06-http协议之响应协议 07-wsgire模块1 08-wsgir ...

  4. python3开发进阶-Web框架的前奏

    我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 1.自定义web框架 import socket ...

  5. 读《架构探险——从零开始写Java Web框架》

    内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...

  6. [转]轻量级 Java Web 框架架构设计

    工作闲暇之余,我想设计并开发一款轻量级 Java Web 框架,看看能否取代目前最为流行的而又越来越重的 Spring.Hibernate 等框架.请原谅在下的大胆行为与不自量力,本人不是为了重造轮子 ...

  7. Web框架本质及第一个Django实例

    Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...

  8. WEB框架-Django框架学习-预备知识

    今日份整理,终于开始整个阶段学习的后期了,今日开始学习Django的框架,加油,你是最胖的! 1.web基础知识 1.1 web应用 Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是 ...

  9. django——web框架简介

    1.web应用 Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件. 应用程序有两种模式C/S.B/S.C/S是客户 ...

随机推荐

  1. springboot项目打war包pom设置

    <build> <finalName>PayManager</finalName><!--打包后的名字PayManager.war--> <plu ...

  2. HDU 1074 Doing Homework 状压dp(第一道入门题)

    Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

  3. listener.ora 与 tnsnames.ora

    listener.ora 是oracle服务器端的网络配置文件,oracle 根据它来配置监听服务. tnsnames.ora 类似于unix 的hosts文件,提供的tnsname到主机名或者ip的 ...

  4. SQL语句查询关键字中含有特殊符号怎么处理, 例如 'SMI_'

    SQL语句查询关键字中含有特殊符号怎么处理, 例如 'SMI_' 错误:select * from emp  where ename like '%SML_%' 正确:select * from em ...

  5. Oracle 12.2 报错:ORA-12012: error on auto execute of job "SYS"."ORA$AT_OS_OPT_SY_7458"

    alert报错 2019-01-12T10:10:11.499130+08:00Errors in file /u01/app/oracle/diag/rdbms/rac1/rac112/trace/ ...

  6. 基于vue-cli3和追书神器制作的移动端小说阅读网站,附接口和源码

    项目简介 基于node express+mysql+vue-cli3和追书神器接口制作的移动端小说阅读网站,**仅供参考学习!不用于任何商业用途!** 闲暇时间用vue练练手,就想写个小说网站来看看, ...

  7. 使用zabbix发送邮件的简易设置流程(存档用)

    1.安装邮件软件 (一般默认安装sendmail,这样apache也不用重新设置.) $sudo yum install sendmail 2.在zabbix上设置发送邮件用的本地邮箱 选择管理-&g ...

  8. 性能测试loadrunner安装

    把杀毒软件关闭 1. 点击 HP_LoadRunner_12.02_Community_Edition_T7177-15059.exe 完成后,点击下一步 接受协议 点击安装 点击完成 TOOLS - ...

  9. LZO压缩算法64位崩溃问题

    *** vs2013 64位调用LZO算法失败,原因: vs2013 long 类型4位 指针为8位. 解决: 将static lzo_bool basic_ptr_check(void)函数中,指针 ...

  10. Mysql linux 安装文档

    1.安装依赖包 yum -y install gcc-c++ ncurses-devel cmake make perl gcc autoconf automake zlib libxml libgc ...