开发者学堂课程【Python 入门 2020年版:简单的 HTTP 服务器搭建】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/639/detail/10477
简单的HTTP服务器搭建
内容介绍:
一、学习目标
二、使用谷歌/火狐浏览器分析
三、概念
四、结合实例百度网站编写服务器
一、学习目标
1.能够说出什么是 HTTP 协议
2.能够手写代码搭建一个 HTTP 服务器
3.了解 WSGI 接口
4.能够看懂自定义 WSGI 服务器
5.能够使用 requests 插件发送请求
今天学习搭建网站,但是这个网站能力差、功能不全、性能不好,只是简单讲一下框架的实现理论。
二、使用谷歌/火狐浏览器分析
1.在 Web 应用中,服务器把网页传给浏览器,实际上就是把网页的 HTML 代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是 HTTP,所以:
HTML 是种用来定义网页的文本,会 HTML, 就可以编写网页;
HTTP 是在网络上传输 HTML 的协议,用于浏览器和服务器的通信。
Chrome 浏览器提供了一套完整地调试工具,非常适合 Web 开发。
安装好 Chrome 浏览器后,打开 Chrome,在菜单中选择"视图",“开发者", “开发者工具", 就可以显示开发者工具:
2.说明
Elements 显示网页的结构
Network 显示浏览器和服务器的通信
我们点 Network,确保第一个小红灯亮着,Chrome 就会记录所有浏览器和服务器之间的通信:
三、概念
HTTP 协议:Hyper Text Transfer Protocol 超文本传输协议
协议的作用就是用来传输超文本 HTML(Hyper Text Markup Language)
HTML:超文本标记语言
HTTP:用来传输超文本的一个协议
有什么规则
我们上网是干什么,怎么写一个网站呢
客户端 Client
HTTP 服务器 Server
C/S架构 client-server 手机淘宝 APP 就是一个客户端
B/S架构 browser-server 浏览器访问淘宝
不管是 APP 还是浏览器我们都可以把它们看作客户端。不管是什么结构就是客户端不一样,服务器的实现都是一样的,返回的数据也是一样的。
如果前后端分离就返回 json 字符串,比如’{“name”:”zhangsan ”}’
四、结合实例百度网站编写服务器
在浏览器里输入192.168.31.199,是不能正常访问操作的。
上网,两个电脑要通信就是网络通信,用 socket 写,其实就是 socket 链接。
1.代码:
import socket
#HTTP服务器都是基于TCP的socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘192.168.31.199’,9090))
server_socket.listen(128)
x=server_socket.accept()
print(x)
用网络调试助手连接一下,会收到消息,获取的数据是一个元祖,元祖里有两个元素,第0个元素是客户端的socket链接,第1个元素是客户端的ip地址和端口号。
2.代码:
import socket
#HTTP服务器都是基于TCP的socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘192.168.31.199’,9090))
server_socket.listen(128)
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的 socket 链接
#第1个元素是 客户端的 ip 地址和端口号
拆包一下:
client_socket,client_addr=server_socket.accept()
#从客户端的 socket 里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
print(data)
在网络调试助手,数据发送处发送 ok,就能在写的服务器中收到 ok。要对底层开发有所认识,但以后写不会用 socket。
128是什么意思,当服务器能够处理1000个,但如果突然同时来了1002个,那这两个就处理不了,需要排队,如果来了1120个,120个也要排队,如果来了1130个,有130个不能处理,128就是排队的长度,剩下的2个就连不进来了。
刚才用网络调试工具访问了服务器,发消息,那能不能用代码发送,以前写过用代码发送一个TCP的请求,自己写了一个服务器。
现在不用客户端,用浏览器。程序已经运行到192.168.31.199:9090(9090是端口号),这里可以用网络调试工具访问,只要能发送一个请求就可以,我们也可以自己写代码发送请求,也能使用浏览器发送请求。
用浏览器是可以收到请求的
上图是 HTTP 的请求头
打开控制栏的两种方法(使用的谷歌浏览器):
(1)右击找到检查
(2)右上角找到浏览器的设置——更多工具——开发者工具
打开后如下图:
先把控制栏打开,这有 Elements,它是界面上的元素。比如打开百度新闻点击一个内容控制栏就会标对应的代码,右击代码选择“delete element”就能删除对应内容,但不是真正删除,只是在自己界面上的元素,刷新删除的元素就会恢复。
我们今天要关注的重点是 network,访问网站时我们发了好多个请求,其中有一个是 www.baidu.com,有的时候我们打开以后会没有了,只需要重新刷新网站,然后再右击检查打开控制栏。经常会发现 network 没有东西,原因是网络没有连接,再刷新一下页面就可以看到了。进入“www.baidu.com”,其中“General”是概要、通用的东西,大家可以简单地看一看,我们先不看这个,先把它收起来。里面有“General”、“Response Headers”、“Request Headers”,后两个分别是“响应头”和“请求头”。
也就是说发的要有规范,返回的也要有规范。
服务器没有给客户端返回消息,9090是空的。
怎么给客户端返回消息:
import socket
#HTTP服务器都是基于TCP的socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘192.168.31.199’,9090))
server_socket.listen(128)
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的socket链接
#第1个元素是 客户端的ip地址和端口号
拆包一下:
client_socket,client_addr=server_socket.accept()
#从客户端的socket里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
#给客户端返回消息
client_socket.send(“hello world”.encode(‘utf8’))
print(data)
这时我们运行一下能不能收到
返回内容之前,需要先设置 HTTP 响应头
怎么设置呢,我们先看一下标准的实例:
点击“view source”(在没点“view source”时,显示的是解析后的内容)就可以看到它的响应头。
import socket
#HTTP服务器都是基于TCP的socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘192.168.31.199’,9090))
server_socket.listen(128)
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的socket链接
#第1个元素是 客户端的ip地址和端口号
拆包一下:
client_socket,client_addr=server_socket.accept()
#从客户端的socket里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
print(‘接收到的数据{}’.format(data))
#返回内容之前,需要先设置HTTP响应头
#设置一个响应头就换一行
client_socket.send(‘HTTP/1.1 200 OK\n’.encode(‘utf8’)) #HTTP/1.1 200 OK是必须写的,是格式
#所有的响应头设置完成以后,再换行
client_socket.send(‘\n’.encode(‘utf8’))
#发送内容
client_socket.send(“hello world”.encode(‘utf8’))
运行结果:
再加一个响应头:client_socket.send(‘content-type:text/html’.encode(‘utf8’))
每一个头写完以后都要换行,所有的头写完以后再来一个换行,紧跟着才是消息。
import socket
#HTTP 服务器都是基于 TCP 的 socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘192.168.31.199’,9090))
server_socket.listen(128)
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的socket链接
#第1个元素是 客户端的ip地址和端口号
拆包一下:
client_socket,client_addr=server_socket.accept()
#从客户端的socket里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
print(‘接收到的数据{}’.format(data))
OK 是必须写的,是格式
client_socket.send(‘content-type:text/html’.encode(‘utf8’))
#所有的响应头设置完成以后,再换行
client_socket.send(‘\n’.encode(‘utf8’))
#发送内容
client_socket.send(“hello world”.encode(‘utf8’))
运行结果:同上,出现 hello world。
这个代码是放在自己电脑上的,我现在把它放到更大的地方,放到腾讯的云服务器上。
但注意地址要改成‘0.0.0.0’,端口号不能写死,地址的区别一会再讲。
运行起来:
我的云端服务器的 ip 地址是:106.54.81.174:9090
可以访问到 hello world
这个不止是我能访问,同学们也能访问,但是我这服务器是一次性的,一次就崩。
修改一下代码:
import socket
#HTTP服务器都是基于TCP的socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘0.0.0.0’,8080))
server_socket.listen(128)
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的 socket 链接
#第1个元素是 客户端的 ip 地址和端口号
拆包一下:
client_socket,client_addr=server_socket.accept()
#从客户端的socket里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
print(‘接收到{}的数据{}’.format(client_addr[0],data))
#返回内容之前,需要先设置HTTP响应头
#设置一个响应头就换一行
client_socket.send(‘HTTP/1.1 200 OK\n’.encode(‘utf8’)) #HTTP/1.1 200 OK是必须写的,是格式
client_socket.send(‘content-type:text/html’.encode(‘utf8’))
#所有的响应头设置完成以后,再换行
client_socket.send(‘\n’.encode(‘utf8’))
#发送内容
client_socket.send(“hello world”.encode(‘utf8’))
现在同学们可以输入地址:106.54.81.174:8080可以访问到 hello world
再改成9090也可以访问到,我这个代码只能接收一次,我没有写死循环,一个同学访问到,其他同学就访问不到了。
换一下端口号,写个死循环:
import socket
port=int(input(‘请输入端口号:’))
#HTTP 服务器都是基于 TCP 的 socket
server_socket=socket.socket(socket.AF_INEF, socket.SOCK_STREAM)
server_socket.bind((‘0.0.0.0’,port))
server_socket.listen(128)
print(‘server is running at 0.0.0.0:{}’.format(port))
#获取的数据是一个元祖,元祖里有两个元素
#第0个元素是 客户端的 socket 链接
#第1个元素是 客户端的 ip 地址和端口号
while True:
client_socket,client_addr=server_socket.accept()
#从客户端的 socket 里获取数据
data=client_socket.recv(1024).decode(‘utf8’)
print(‘接收到{}的数据{}’.format(client_addr[0],data))
#返回内容之前,需要先设置 HTTP 响应头
#设置一个响应头就换一行
client_socket.send(‘HTTP/1.1 200 OK\n’.encode(‘utf8’)) #HTTP/1.1 200 OK 是必须写的,是格式
client_socket.send(‘content-type:text/html’.encode(‘utf8’))
#所有的响应头设置完成以后,再换行
client_socket.send(‘\n’.encode(‘utf8’))
#发送内容
client_socket.send(“hello world”.encode(‘utf8’))
现在把代码上传到服务器,让大家玩一玩,原理还是很简单的。
是可以访问到的,端口号我改成9090,不断变,因为一直提示占用,这个服务器比较脆弱。
现在不返回 hello world,我把你们的 ip 地址返回去。不只是电脑可以访问,手机也可以。