Python HTTP协议编程

4 阅读9分钟

HTTP协议

HTTP协议属于建立在TCP协议中的应用层上的一种协议,HTTP协议以客户端请求和服务端应答为标准

浏览器通常被人称为客户端,Web服务器常被称作服务端

HTTP协议常用端口为80,客户端首先通过80端口向HTTP服务端发起请求,建立TCP连接;之后进行HTTP数据传输

协议
HTTP应用层
SSL/TLS加密层
TCP传输层
IP传输层
数据链路层物理层

当浏览器作为客户端访问服务器之后,取到所有所需的数据,立即断开TCP连接,整个HTTP连接过程非常短,所以人们也常称HTTP协议为无连接的协议

这也因为每一次HTTP的请求,都是重新开启一个新的连接,而不是在一个历史连接持续工作

资源定位标识符

发起HTTP请求的内容资源由统一资源标识符(Uniform Resource Identifiers)URI进行表示

其中资源定位标识符主要有三种

  • URI:统一资源标识符(uniform resource identifier);唯一表示一个资源。
  • URN:统一资源命名(uniform resource name);通过名字来表示资源。
  • URL:统一资源定位器(uniform resource locaotor);一个具体的URI标示,可以通过该标识获取访问到对应资源

URI

其中如果已经是一个URL,那么肯定是一个URI标识;但是一个URI并不一定是一个URL;URL是URI的一种表现形式

URL

URL是资源标识符,而URL不光具有标识性,还需要具有定位性,用来更加具体描述资源的具体位置,并且还会指明获取资源所采用的协议。一个URL包含,主机名称(IP或域名)、端口号(默认为80)、路径和查询字符串5部分

http://www.example.com:80/aaa/test.jpg?id=1
  • 网络协议为:http
  • 主机:www.example.com
  • 端口:80
  • 路径:aaa/test.jpg
  • 查询字符串参数:id=1

URN

URN也是URI的一种表现形式,包括名字(给定的命名空间),但是不包括访问的方式

www.example.com/aaa/test.jpg

image-20200430092157918转存失败,建议直接上传图片文件

Request

当我们使用HTTP协议访问某个连接时,首先需要向服务器提交一个Request请求;

一般当我们使用浏览器访问Web服务时,由浏览器完成这个工作,Request消息分为三部分,分别包括:Request Line、Request Header、Body

下面为HTTP协议在Request时的消息体构造:

请求方法URL协议版本\r\nRequest Line
头部字段**:**冒号对应字段\r\nRequest Header
头部字段**:**冒号对应字段\r\nRequest Header
其他头部字段属性…Request Header
\r\n在头部信息之后必须要有该换行回车符号
其他数据Body
  • 一个通过curl命令获取到的访问Web服务时的信息
curl -v https://baidu.com
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: baidu.com
> Accept: */*
> 

请求方式

请求解释
GET获取服务端数据;
POST向服务端提交数据;
PUT向服务端上传数据;
DELETE删除服务端通过Request-URL所标示的资源;
TRACE测试服务端是否可以接收到Request请求;
CONNECT以管道方式连接代理服务器;
OPTIONS返回服务器所支持的其他HTTP请求方法;
HEAD与GET方法类似,但不返回服务器响应时的消息体;

Response响应

服务器接收到之后,会向我们返回一个Response响应,浏览器接收到了Response之后,会帮助我们对信息进行解析,之后我们就可以看到对应的Web页面及获取到的资源

下面是HTTP协议在Response响应时的消息体构造

响应版本状态码状态信息\r\nResponse Line
头部字段**:**冒号对应字段\r\nResponse Header
头部字段**:**冒号对应字段\r\nResponse Header
他头部字段属性…
\r\n在头部信息之后必须要有该换行回车符号
响应数据Body
  • 可以使用curl -v命令查看到Response信息
< HTTP/1.1 302 Moved Temporarily
< Server: bfe/1.0.8.18
< Date: Thu, 30 Apr 2020 01:33:50 GMT
< Content-Type: text/html
< Content-Length: 161
< Connection: keep-alive
< Location: http://www.baidu.com/
< 
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>bfe/1.0.8.18</center>
</body>
</html>

状态码

状态码状态码英文标示意义
100ContinueHTTP/1.1中新增状态码,表示客户端可以继续请求HTTP服务器;
101Switching Protocols服务端切换请求协议,切换到HTTP协议新版本;
200OK客户端的请求服务端正常完成;
301Moved Permanently客户端请求的资源已被永久移动到新的URL;
302Found客户顿请求的资源被临时移动,客户端继续使用原有URL;常用于三方登录之后的跳转;
304Not Modified请求的资源未被修改,可以继续访问原URL 常用于缓存;
400Bad Request客户端的请求语法错误,或无法解析请求;
401Unauthorized请求需要经过身份验证;
402Payment Required保留状态码,为以后使用做准备的呢;
403Forbidden服务端直接拒绝客户端的请求;
404Not Found客户端请求的资源找不到;
405Method Not Allowed客户端请求的方式不被允许;
406Not Acceptable客户端请求的内容无法正常完成;
499Client has closed Connection服务端处理该请求花费了太长的时间;
500Internal Server Error服务端内部错误; 可能是因为Web服务配置文件读取错误 也可能是因为用户权限等等问题导致…
502Bad Geteway服务端内部错误; 服务端错误的网关;
503Service Unavailable服务端无法响应客户端的请求;
504Gateway Time-out服务端处理请求超时;或者可能是访问的网管超时;
505HTTP Version not Supported客户端请求的HTTP协议版本无法被服务端支持; 可能是你的浏览器太古老了;

请求报文字段

我们看到的请求头,就是当我们通过浏览器访问一个URL时,会向服务器发送的,我们经常叫这个为Request Headers(请求头);在请求头中,维护了很多字段信息,这些常见的HTTP请求头中响应字段的详细解释如下

参考地址:en.wikipedia.org/wiki/List_o…

Accept: text/html指定客户端支持接收的数据类型(MIME类型)。
Accept-Charset: utf-8指定客户端可接受的字符集,缺省表示任何字符集都可以接受;
Accept-Encoding: gzip, deflate指定客户端支持的Web服务器返回内容压缩编码类型。
Accept-Language: zh-CN指定客户端可以接受的语言;
Authorization:Basic xxx=HTTP认证的权限;
Cache-Control: no-cache指定所有缓存机制必须遵从的指令;
Connection: keep-alive表示是否需要持久连接(HTTP1.1协议中默认进行持久连接);
Content-Length: 123请求的内容长度;
Content-Type: text/html用于在post及put时请求的媒体类型;
Cookie: $XXX保存在该请求域名下的COOKIE值信息,常用于表示用户的历史访问记录;
Date: Tue, 15 Nov请求发送的日期时间;
Referer: …访问到这个站点之前的地址,用来指定访问从何处而来;
User-Agent: …指定当前访问者的用户信息,经常为一些浏览器、操作系统属性;

响应报文字段

Age: 775从原始服务器到代理缓存形成的时间估算(秒为单位);
Allow: GET,HEAD指定资源的有效请求行为,不允许请求则返回405;
Cache-Control: max-age=3600缓存机制,max-age参数未缓存有效时间(秒为单位);
Content-Encoding: gzip响应内容的支持的压缩编码类型
Content-Disposition: attachment; filename="fname.ext"可下载资源属性(MIME二进制格式),提供文件下载的对话框,并默认文件名为filename;
Content-Length: 348响应的数据长度;
Content-Language: en-US响应的数据语言格式;
Content-Location: /index.htm响应资源的替代地址;
Content-Type: text/html; charset=utf-8响应数据的MIME类型;
Date: Tue, 15 Nov…服务器发送响应时的日期时间;
Expires: Tue, 15 Nov…响应过期的日期时间;
Server: NginxWeb服务器名称;
Set-Cookie: username=test; Max-Age=3600;设置HTTP响应中的cookie值;

MIME类型

经常我们的服务端会返回很多类型的内容给到客户端,这就需要在返回的过程中在Response中指明返回的文件类型,然后浏览器等各种客户端工具可以通过解析在Response中涉及MIME类型对应文件使用已有应用程序来解释各种文件类型。

其中MIME支持属性有如下:

  • application/msexcel:微软Execl文件;.xls .xla;
  • application/mshelp:微软windows帮助文件;.hlp .chm
  • application/mspowerpoint:微软PowerPoint文件;.ppt .ppz .pps .pot
  • application/msword:微软Word文件;.doc .dot;
  • application/octet-stream:二进制可执行文件;.exe;
  • application/pdf:Adobe PDF文件;.pdf;
  • application/post******:Adobe post***文件;.ai .eps .ps;
  • application/rtf:微软rtf文件;.rtf;
  • application/x-httpd-php:PHP文件;.php .phtml;
  • application/x-java******:服务端的java***文件;.js;
  • application/x-shockwave-flash:Shockwave-Flash文件;.swf .cab;
  • application/zip:ZIP存档文件;.zip;
  • audio/basic:音频文件;.au .snd;
  • audio/mpeg:MPEG文件;.mp3;
  • audio/x-midi:MIDI文件;.mid .midi;
  • audio/x-mpeg:MPEG文件;.mp2;
  • audio/x-wav:Wav文件;.wav;
  • image/gif:Gif文件;.gif;
  • image/jpeg:JPEG文件;.jpeg .jpg .jpe;
  • image/x-windowdump:X-Windows存储文件;.xwd;
  • text/css:css样式表文件;.css;
  • text/html:html类型;.html .htm .shtml;
  • text/java******:Java***文件;.js;
  • text/plain:文本类型;.txt;
  • video/mpeg:MPEG文件;.mpeg .mpg .mpe;
  • video/vnd.rn-realvideo:在线播放视频文件;.rmvb;
  • video/quicktime:Quicktime文件,苹果公司提供的视频文件格式;.qt .mov;
  • video/vnd.vivo:Vivo文件;.viv .vivo;

构造Web客户端及服务端

  • 一个待返回的HTML页面
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>This is a Test</title>
</head>
<body>
    <div>你好,旅行者。</div>
</body>
</html>
  • 编写服务端代码返回该网页
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 8001))
with open('1.html', encoding='utf-8') as fp:
    data = fp.read()

response = "HTTP/1.1 200 OK \r\n" + \
    "Server: abc \r\n" + \
    "Content-Type: text/html;charset=utf-8\r\n" + \
    "\r\n" + \
    data
s.listen(5)
while 1:
    try:
        c, c_addr = s.accept()
        print(c.recv(1024).decode())
        c.send(response.encode())
        c.close()
    except KeyboardInterrupt:
        break
s.close()
  • 请求客户端代码
import socket

c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
msg = "GET / HTTP/1.1\r\n" + \
      "Host: 127.0.0.1\r\n"+ \
      "Accept: text/html\r\n" + \
      "\r\n"

c.connect(('127.0.0.1',80))
c.send(msg.encode())
data = c.recv(1024).decode()
print('[+] msg from Server:\n%s' % data)