1.Http协议介绍
HTTP 协议 全程,中文超文本传输协议 超文本是超级文本的缩写,是指超越文本限制或超链接,比如:图片、音乐、视频、超链接等都属于超文本 HTTP (1991)设计的目的:传输网页数据,现在允许传输任意类型的数据 传输HTTP协议(通信数据的格式)格式的数据是基于TCP 传输协议的,发送数据之前先建立连接
-
HTTP协议的作用 它规定了浏览器和Web服务器通信数据的格式,也就是说浏览器和web服务器通信需要使用http协议 -
浏览器访问web服务器的通信过程 web服务器程序默认端口号为80
- 浏览器输入网址–通过DNS(域名解析服务器)将域名解析成ip地址
- 客户端获取到ip地址,端口号(web服务程序默认端口号为80)
- 与web服务器程序建立连接(HTTP是基于TCP的)
- 发送http请求数据到web服务器程序(http格式的数据)
- web服务器根据请求获取本地服务器主机资源
- 本地服务器主机返回资源到web服务器
- web服务器返回http响应数据到浏览器
2.URL
URL 表达的是统一资源定位符,通俗讲就是网络资源地址,也是常说的网址
- URL组成:
- 样式:https://news.163.com/18/1122/10/E178J2O.html
- 协议部分:https:// (会对http://的数据进行加密 端口号 443) http://(端口号为80) ftg://
- 域名部分:news.163.com
- 资源路径:18、/34fds3245fs.html
- 查询参数部分(可选)
- 域名就是ip地址的别名,是用点进行分割使用英文字母和数字组成的名字,使用域名目的就是方便记忆某台ip地址
- url https://news.163.com/heellooe.html?page=1&count=10
- ? 后面的参数 都 使用& 连接
3.HTTP请求报文
- http请求报文(浏览器发送给web服务器程序的http协议数据)
- http最常见的请求报文有两种:
- GET 方式请求报文 获取web服务器数据 如:新闻列表
- POST 方式请求报文 向web服务器提交数据以及获取数据 如:登录
- HTTP GET 请求报文
- 格式:
- 请求行 \r\n
- 请求头 、\r\n
- 空行 \r\n
- 每项信息之间都需要一个 \r\n ,不能有空格,要http协议规定
- 请求报文 请求头–请求行–空行
- GET/HTTP/1.2 请求方式 请求资源路径 http协议版本
- HOST: 服务器主机ip地址和端口号,默认端口号是80
- Connection:Keep-alive 和服务端操持长链接,当客户端和服务端有一段时间(3-5)没有进行通信,服务器程序就会主动向客户端断开连接
- Upgrade-Insecure-Requests:1 让客户端请求不安全请求,以后会用https
- User-Agent 用户代理,客户端程序名称,
- Accept 告诉服务端程序可以接受的数据类型
- Accept-Encoding: 告诉服务端程序支持的压缩算法
- Accept-Language: 告诉服务端程序支持的语言
- Cookie 客户端用户身份的标识(唯一的)
- HTTP POST 请求报文
- GET 请求格式
- Form Data 请求体,浏览器发送给服务器的数据(可以没有但很少)
- HTTP 响应报文(服务端程序传过来的数据)响应行–响应头–空行–响应体
- HTTP/1.1 200 OK HTTP协议版本 状态码 状态描述(响应行)
- Server 服务器的名称
- Content-Type 服务器发送给浏览器的内容类型及编码格式
- Transfer-Encoding 服务器发送给客户端程序(浏览器)的数据不确定数据长度 数据发送结束的接收标识 :0\r\n Content-Length:200(字段) 服务器发送给客户端程序的数据确定长度
- Connection :Keep-alive 和客户端保持长连接
- Data 服务器的时间 (0时区时间 北京是东八区 差8个小时)
- 剩下是自定义响应头信息,自己定义响应头的名字和响应头的信息
- —空行—
- —响应体—
- 每一项都有 \r\n
- –响应体 就是真正意义上给浏览器解析使用的数据–(Response里的)
- 对于请求头和响应头信息程序员都可以自定义,按照客户端和服务器约定好的发方式进行来判定即可
- 状态码(用于标识web服务器响应状态的3位数字代码)
- 200 请求成功
- 307 重定向
- 400 错误请求,请求地址或者参数有误
- 404 请求资源在服务器不存在
- 500 服务器内部资源代码出现错误
4.搭建Python自带静态Web服务器
- 静态Web服务器是什么?
可以为发出请求的浏览器提供静态文档的程序, 我们平时浏览百度新闻数据的时候,每天的新闻数据都是会发生变化,此访问的页面就是动态的,而我们开发的是静态的,页面的数据不会发生改变 - 如何搭建Python自带的静态Web服务器
- 搭建Python自带的静态Web服务器 使用 python3 -m http.server 端口号(默认是8000)
- -m 表示运行包里面的模块,执行这个命令的时候,需要进入自己指定的静态文件的目录,然后通过浏览器就能访问对应的html 文件了,这样一个静态的Web服务器就搭建好了
5.搭建自己的静态Web服务器
- 步骤
- 编写一个TCP服务端程序
- 获取浏览器发送的http请求报文数据
- 读取固定页面的数据,把页面数据组装成HTTP响应报文数据发送给浏览器
- HTTP响应报文数据发送完成后,关闭服务于客户端的套接字
- 搭建静态Web服务器–返回固定页面数据
- 创建服务端套接字对象
- 设置服务端套接字复用
- 绑定端口号
- 设置监听(最大监听数为128)
- 等待接收客户端连接请求,拿到客户端传来的通信套接字 (循环等待)
- 接收客户端的请求想信息 (数据量大概在4k多)
- with open 打开文件 读取数据(文件路径为html 文件路径,encoding=‘utf-8’(可选))
- 响应行 response_line=‘HTTP协议版本 状态码 状态描述 \r\n’
- 响应头 response_header=‘Server: 服务器名称(一般为python的) \r\n’
- 空行
- 响应体 response_body=f_data(读物到的文件中数据)
- 把数据封装成http 响应报文的数据格式 response=response_line+response_header+’\r\n’+response_body
- 将字符串编码为二进制 response_data=response.encode(‘utf-8’)
- 将响应报文数据发送给浏览器 new_socket.send(response_data)
- 关闭客户端套接字
import socket
if __name__=='__main__':
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',8000))
tcp_server_socket.listen(128)
while True:
new_socket,iport=tcp_server_socket.accept()
recv_data=new_socket.recv(4096)
print(recv_data)
with open('wenjian/templates/index.html',encoding='utf-8') as f:
f_data=f.read()
response_line='HTTP/2 200 OK\r\n'
response_header='Server: PWS/2.0\r\n'
response_body=f_data
response=response_line+response_header+'\r\n'+response_body
response_data=response.encode('utf-8')
new_socket.send(response_data)
new_socket.close()
6.静态服务器-返回指定页面内容
- 创建服务端套接字对象
- 设置端口号复用
- 绑定端口号
- 设置监听
- 等待客户端连接请求 得到新的通信套接字 (循环保持服务长启)
- 接收客户端的请求信息 并对之解码 (需要判断接收的数据长度是否为0,防止客户端不发送信息,造成服务器奔溃)(请求报文的请求头有页面信息)
- 利用空格分割得到指定页面名称
- 打开指定文件(为图片能够加载 使用 rb 模式)(判断客户端请求是否为根目录,设置默认值)
- 响应行
- 响应头
- 空行
- 响应体=文件读取的数据
- 将数据封装成 http 格式 并转换为字节码格式
- 发送http协议数据
- 关闭客户端套接字
'''返回指定页面数据'''
import socket
def main():
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',8000))
tcp_server_socket.listen(128)
while True:
new_socket,iport=tcp_server_socket.accept()
recv_data_en=new_socket.recv(4096)
if len(recv_data_en) == 0:
new_socket.close()
return
recv_data=recv_data_en.decode('utf-8')
recv_data_list=recv_data.split(' ',maxsplit=2)
request_path=recv_data_list[1]
print(request_path)
if request_path=='/':
request_path='/index.html'
with open('wenjian'+request_path,'rb') as f:
f_data=f.read()
response_line='HTTP/2 200 OK\r\n'
response_header='Server: PWS/2.0\r\n'
response_body=f_data
response=(response_line+response_header+'\r\n').encode('utf-8')+response_body
new_socket.send(response)
new_socket.close()
if __name__ == '__main__':
main()
7.返回404页面
在原有基础上添加 判断请求页面是否存在
- 通过os.path.exits
- 通过异常处理
'''返回404'''
import socket
import os
def main():
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',8000))
tcp_server_socket.listen(128)
while True:
new_socket,iport=tcp_server_socket.accept()
recv_data=new_socket.recv(4096)
if len(recv_data)==0:
new_socket.close()
return
recv_datam=recv_data.decode('utf-8')
request_li=recv_datam.split(' ',maxsplit=2)
request=request_li[1]
if request=='/':
request='/index.html'
print(request)
try:
with open('wenjian'+request,'rb') as f:
f_data=f.read()
except Exception as e:
response_line = 'HTTP/2 404 Not Found\r\n'
response_header = 'Server PWS/2.0\r\n'
with open('wenjian/error.html',encoding='utf-8') as f:
f_data=f.read()
response_body=f_data
response = response_line + response_header + '\r\n'+ response_body
new_socket.send(response.encode('utf-8'))
else:
response_line='HTTP/2 200 OK\r\n'
response_header='Server PWS/2.0\r\n'
response_body=f_data
response=(response_line+response_header+'\r\n').encode('utf-8')+response_body
new_socket.send(response)
finally:
new_socket.close()
if __name__ == '__main__':
main()
8.多任务
通过应用线程
'''多任务'''
import socket,os,threading
def th_accept(new_socket):
recv_data = new_socket.recv(4096)
if len(recv_data) == 0:
new_socket.close()
return
recv_d = recv_data.decode('utf-8')
recv_li = recv_d.split(' ', 2)
request = recv_li[1]
if request == '/':
request = '/index.html'
if not os.path.exists('wenjian' + request):
with open('wenjian/error.html', encoding='utf-8') as f:
f_data = f.read()
response_line = 'HTTP/2 404 Not Found\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = response_line + response_header + '\r\n' + response_body
new_socket.send(response.encode('utf-8'))
new_socket.close()
else:
with open('wenjian' + request, 'rb') as f:
f_data = f.read()
response_line = 'HTTP/2 200 OK\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
new_socket.send(response)
new_socket.close()
def main():
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',8000))
tcp_server_socket.listen(128)
while True:
new_socket,iport=tcp_server_socket.accept()
sub_thread=threading.Thread(target=th_accept,args=(new_socket,),daemon=True)
sub_thread.start()
if __name__ == '__main__':
main()
9.面向对象开发
- 步骤
- 把提供服务的web服务器抽象成一个类(HTTPWebServer)
- 提供Web服务器的初始化方法,在初始化方法里面创建socket对象
- 提供一个开启web服务器的方法,让Web服务器处理客户端请求
'''面向对象的'''
import socket,os,threading
class HttpWebServer(object):
def __init__(self):
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',8000))
tcp_server_socket.listen(128)
self.tcp_server_socket=tcp_server_socket
@staticmethod
def th_accept(new_socket):
recv_data = new_socket.recv(4096)
if len(recv_data) == 0:
new_socket.close()
return
recv_d = recv_data.decode('utf-8')
recv_li = recv_d.split(' ', 2)
request = recv_li[1]
if request == '/':
request = '/index.html'
if not os.path.exists('wenjian' + request):
with open('wenjian/error.html', encoding='utf-8') as f:
f_data = f.read()
response_line = 'HTTP/2 404 Not Found\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = response_line + response_header + '\r\n' + response_body
new_socket.send(response.encode('utf-8'))
new_socket.close()
else:
with open('wenjian' + request, 'rb') as f:
f_data = f.read()
response_line = 'HTTP/2 200 OK\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
new_socket.send(response)
new_socket.close()
def start(self):
while True:
new_socket, iport = self.tcp_server_socket.accept()
sub_thread = threading.Thread(target=self.th_accept, args=(new_socket,), daemon=True)
sub_thread.start()
def main():
web_server=HttpWebServer()
web_server.start()
if __name__ == '__main__':
main()
10.获取终端命令行参数及启动动态绑定端口号
使用sys模块 params=sys.argv print(params) #是列表形式
- 步骤
- 获取执行python程序的终端命令行参数
- 判断参数的类型,设置端口号必须是整形
- 给web服务器类初始化方法添加一个动态端口号参数,用于绑定端口号
- linux 终端启动 用Python3
- windows 终端启动 用Python(在文件目录下)
import sys
import socket,os,threading
class HttpWebServer(object):
def __init__(self,port):
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
tcp_server_socket.bind(('',port))
tcp_server_socket.listen(128)
self.tcp_server_socket=tcp_server_socket
@staticmethod
def th_accept(new_socket):
recv_data = new_socket.recv(4096)
if len(recv_data) == 0:
new_socket.close()
return
recv_d = recv_data.decode('utf-8')
recv_li = recv_d.split(' ', 2)
request = recv_li[1]
if request == '/':
request = '/index.html'
if not os.path.exists('wenjian' + request):
with open('wenjian/error.html', encoding='utf-8') as f:
f_data = f.read()
response_line = 'HTTP/2 404 Not Found\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = response_line + response_header + '\r\n' + response_body
new_socket.send(response.encode('utf-8'))
new_socket.close()
else:
with open('wenjian' + request, 'rb') as f:
f_data = f.read()
response_line = 'HTTP/2 200 OK\r\n'
response_header = 'Server PWS/2.0\r\n'
response_body = f_data
response = (response_line + response_header + '\r\n').encode('utf-8') + response_body
new_socket.send(response)
new_socket.close()
def start(self):
while True:
new_socket, iport = self.tcp_server_socket.accept()
sub_thread = threading.Thread(target=self.th_accept, args=(new_socket,), daemon=True)
sub_thread.start()
def main():
params=sys.argv
if len(params)!=2:
print('执行命令格式:python3 xxx.py 9000')
return
if not params[1].isdigit():
print('执行命令格式:python3 xxx.py 9000')
return
port=int(params[1])
print(params)
web_server=HttpWebServer(port)
web_server.start()
if __name__ == '__main__':
main()
|