资讯专栏INFORMATION COLUMN

写一个简单的webserver

yuanxin / 1786人阅读

摘要:基于写的极简版。服务器是属于被动的一方,当浏览器发起请求的时候,服务器才能和浏览器通信,在此之前,服务器都处于一个等待监听的状态。监听本地端口这是一个极简的,需要注意的是,我们仅实现了协议的部分。

基于 Python3 写的极简版 webserver。用于学习 HTTP协议,及 WEB服务器 工作原理。笔者对 WEB服务器 的工作原理理解的比较粗浅,仅是基于个人的理解来写的,存在很多不足和漏洞,目的在于给大家提供一个写 webserver 的思路。项目GitHub地址:https://github.com/hanrenguang/simple-webserver。

WEB服务器原理

学过计网的同学应该都知道 HTTP协议 是在 TCP协议 之上实现的。浏览器与服务器之间的通信首先是建立 TCP 连接,再进行请求和响应报文的传输。服务器是属于被动的一方,当浏览器发起请求的时候,服务器才能和浏览器通信,在此之前,服务器都处于一个等待监听的状态。

socket连接

实现服务器的第一步是建立一个 socket 连接,socket 套接字是对 TCP/UDP协议 的一个封装,Python 就自带有 socket 模块,所以使用起来很方便。

</>复制代码

  1. import socket
  2. sk = socket.socket(
  3. socket.AF_INET,
  4. socket.SOCK_STREAM
  5. )
  6. # 监听本地 8888 端口
  7. host = "127.0.0.1"
  8. port = 8888
  9. sk.bind((host, port))
  10. sk.listen(5)
  11. while True:
  12. try:
  13. clientSk, addr = sk.accept()
  14. print("address is: %s" % str(addr))
  15. req = clientSk.recv(1024)
  16. clientSk.sendall("...")
  17. clientSk.close()
  18. except Exception as err:
  19. print(err)
  20. clientSk.close()

这是一个极简的 socket-server,需要注意的是,我们仅实现了 TCP协议 的部分。

解析HTTP请求

拿到浏览器的请求很简单,clientSk.recv() 即可获取请求报文,而些数据我们无法直接拿来用,因为它是基于 HTTP协议 封装的数据,在我们进行下一步操作前,需要对请求报文“解封”。而在此之前,我们需要了解请求报文的格式。最快捷的方式呢,是打开浏览器(以 chrome 为例),随便打开百度啥的,F12 打开开发者工具,在 Network 一栏就可以观察到。大概长下面这样:

</>复制代码

  1. GET / HTTP/1.1
  2. Host: xxx
  3. Connection: xxx
  4. Cache-Control: xxx
  5. Upgrade-Insecure-Requests: xxx
  6. User-Agent: xxx
  7. Accept: xxx
  8. Accept-Encoding: xxx
  9. Accept-Language: xxx
  10. Cookie: xxx

我们把关注点放在第一行,GET 方法,请求的资源路径为 /,使用的协议是 HTTP1.1,之后就是一回车换行符 。所以我们对报文的解析如下(存在许多不足之处):

</>复制代码

  1. # 第一步先对数据进行解码 decode(),
  2. # 再以行为单位进行分割
  3. requestList = clientSk.recv(1024).decode().split("
  4. ")
  5. # 调用写好的函数对其进行解析
  6. parseReq(requestList)
  7. # 解析请求报文
  8. def parseReq(reqList):
  9. # 保存解析结果
  10. parseRet = {}
  11. # 请求的方法,如 GET
  12. method = reqList[0].split(" ")[0]
  13. # 请求的资源路径,如 "/"
  14. sourcePath = reqList[0].split(" ")[1]
  15. parseRet["method"] = method
  16. parseRet["sourcePath"] = sourcePath
  17. i = len(reqList) - 1
  18. # 以 key: value 的形式保存解析结果
  19. while i:
  20. if len(reqList[i].split(":")) == 1:
  21. i = i - 1
  22. continue
  23. idx = reqList[i].find(":")
  24. key, value = reqList[i][0:idx], reqList[i][idx+1:]
  25. parseRet[key] = value.strip()
  26. i = i - 1
  27. return parseRet
构造响应报文

拿到了请求报文并将其解析后,我们可以开始构造响应报文的内容了,以请求静态资源为例,假设请求报文第一行为 GET /index.html HTTP/1.1。那么我首先要做的就是先获取路径为 /index.html 的文件内容:

</>复制代码

  1. # 获取资源内容
  2. try:
  3. f = open(path, "r")
  4. while True:
  5. chunk = f.read(1024)
  6. if not chunk:
  7. f.close()
  8. break;
  9. content += chunk
  10. except:
  11. pass

那接下来就是构造响应报文了,同理可以观察 HTTP 响应报文的格式,在此就不举例了,直接上代码:

</>复制代码

  1. try:
  2. f = open(path, "r")
  3. while True:
  4. chunk = f.read(1024)
  5. if not chunk:
  6. f.close()
  7. break;
  8. content += chunk
  9. except:
  10. pass
  11. # 省略了大部分头部信息
  12. headers = "HTTP/1.1 200 OK
  13. "
  14. contentType = "Content-Type: text/html; charset=utf-8
  15. "
  16. contentLen = "Content-Length: " + str(len(content)) + "
  17. "
  18. # 组合成响应报文 res
  19. res = headers + contentType + contentLen + "
  20. " + content
  21. # 编码后发送给浏览器,
  22. # 至此,本次通信结束
  23. clientSk.sendall(res.encode(encoding="UTF-8"))
  24. clientSk.close()
示例

到项目GitHub:https://github.com/hanrenguang/simple-webserver,下载本项目到本地,双击 server.py,并访问 http://localhost:8888/index.html,你应该会看到十分亲切的 Hello world!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/38625.html

相关文章

  • Oracle发布开源轻量级 Java 微服务框架 Helidon

    摘要:近日,推出了一个新的开源框架,该项目是一个用于创建基于微服务的应用程序的库集合。下图说明了和所属的微服务框架类别。启用后,会将其跟踪事件发送到。 近日,Oracle推出了一个新的开源框架Helidon,该项目是一个用于创建基于微服务的应用程序的Java库集合。和Payara Micro、Thorntail(之前的WildFly Swarm)、OpenLiberty、TomEE等项目一样...

    Benedict Evans 评论0 收藏0
  • 浅析微服务框架 Helidon 使用

    摘要:零前期准备版本版本核心依赖包支持包简介是官方出品的微服务框架,底层基于驱动,大致的使用套路和相差不是很多笔者只是浅浅的了解过,可能存在理解不透的情况。一配置中的配置类有两种,一种是用于读取配置文件的,另一种是用于配置服务器对象的。 零 前期准备 0 版本 JDK 版本 : OpenJDK 11.0.1 IDE : idea 2018.3 Helidon Webserver : heli...

    dockerclub 评论0 收藏0
  • 使用gulp配置代理,简单解决前端跨域问题

    摘要:配置代理,解决跨域问题简单的解决跨域问题,有实时重新加载功能,适用于偶尔改个文件,或者活动页上面有简单的前后端交互初始化全局安装本地下载包下载包新建文件实时重新加载启动时默认浏览器打开的文件代理的域名因为升级到了,所以使 gulp配置代理,解决跨域问题 简单的解决跨域问题,有实时重新加载功能,适用于偶尔改个文件,或者H5活动页上面有简单的前后端交互 npm init初始化 全局安装gu...

    taohonghui 评论0 收藏0
  • 用gulp管理自己前端开发任务及需要注意

    摘要:但是频繁的关闭服务与重启服务,这样就造成了很多时间浪费,所以我们需要利用来监视文件的改动,并将这些改动重新发布到生产目录,并重启服务非手动。三预处理器文件编译暂时没用到,后面用到再增加,可以参考其他人的 关于gulp,grunt,webpack,刚走前端模块化的我,真的是傻傻分不清楚,幸好有大神各种答疑解惑,使我略知一二,你也想知道的,也许还想知道点啥,资源罗列:1、中文官方文档;2、...

    gplane 评论0 收藏0

发表评论

0条评论

yuanxin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<