自己动手写http服务器(二) -- http协议分析

自己动手写http服务器(一) -- UNIX C 网络编程
自己动手写http服务器(二) -- http协议分析
自己动手写http服务器(三) -- 代码实现

要编写一个 http 服务器,第一步就是分析 http 协议格式,之后才能对发送过来的http数据包进行正常解析,并返回正确的数据包;

Http协议包的格式

首先,让我们用 netcat 捕获浏览器发送给服务器的数据包,来见一见其庐山真面目。

(1)捕捉 http 协议的数据包

通过命令:

nc  -l  127.0.0.1 8888  >  http.data

   
   

开启本地的 8888 号端口,在浏览器中输入 url 地址 http://127.0.0.1:8888 ,浏览器将会发送给一个Get请求给nc,nc将接收到的数据写入文件 http.data , 接收到的内容如下:


   
   
  1. 00000000: 4745 5420 2f 20 4854 5450 2f 31 2e 31 0d 0a GET / HTTP/ 1. 1..
  2. 00000010: 486f 7374 3a 20 3132 372e 302e 302e 313a Host: 127.0.0.1:
  3. 00000020: 3838 3838 0d 0a 436f 6e 6e 6563 7469 6f 6e 8888..Connection
  4. 00000030: 3a 20 6b 65 6570 2d 61 6c 69 7665 0d 0a 5570 : keep-alive..Up
  5. 00000040: 6772 6164 652d 496e 7365 6375 7265 2d 52 grade-Insecure-R
  6. 00000050: 6571 7565 7374 733a 2031 0d 0a 5573 6572 equests: 1..User
  7. 00000060: 2d 41 6765 6e 74 3a 20 4d 6f 7a 69 6c 6c 612f -Agent: Mozilla/
  8. 00000070: 352e 3020 2858 3131 3b 20 4c 69 6e 75 7820 5. 0 (X 11; Linux
  9. 00000080: 7838 365f 3634 2920 4170 706c 6557 6562 x 86_ 64) AppleWeb
  10. 00000090: 4b 69 742f 3533 372e 3336 2028 4b 48 544d Kit/ 537. 36 (KHTM
  11. 000000a0: 4c 2c 206c 696b 6520 4765 636b 6f 29 2043 L, like Gecko) C
  12. 000000b0: 6872 6f 6d 652f 3539 2e 30 2e 33 3037 312e hrome/ 59. 0. 3071.
  13. 000000c0: 3131 3520 5361 6661 7269 2f 35 3337 2e 33 115 Safari/ 537. 3
  14. 000000d0: 360d 0a 41 6363 6570 743a 2074 6578 742f 6..Accept: text/
  15. 000000e0: 6874 6d 6c 2c 61 7070 6c 69 6361 7469 6f 6e html,application
  16. 000000f0: 2f 78 6874 6d 6c 2b 78 6d 6c 2c 61 7070 6c 69 /xhtml+xml,appli
  17. 00000100: 6361 7469 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 cation/xml;q= 0. 9
  18. 00000110: 2c 69 6d 61 6765 2f 77 6562 702c 696d 6167 ,image/webp,imag
  19. 00000120: 652f 6170 6e 67 2c 2a 2f 2a 3b 71 3d 30 2e 38 e/apng,*/*;q= 0. 8
  20. 00000130: 0d 0a 4163 6365 7074 2d 45 6e 63 6f 64 696e ..Accept-Encodin
  21. 00000140: 673a 2067 7a 69 702c 2064 6566 6c 61 7465 g: gzip, deflate
  22. 00000150: 2c 20 6272 0d 0a 4163 6365 7074 2d 4c 616e , br..Accept-Lan
  23. 00000160: 6775 6167 653a 207a 682d 434e 2c 7a 683b guage: zh-CN,zh;
  24. 00000170: 713d 302e 382c 6c 61 3b 71 3d 30 2e 36 2c 64 q= 0. 8,la;q= 0. 6,d
  25. 00000180: 613b 713d 302e 340d 0a 0d 0a a;q= 0. 4....

左侧是接收的数据原始二进制流,右侧是对应的ASCII码;

可见浏览器默认使用的http协议是 HTTP/1.1,其头信息肯定是文本(ASCII编码);

(2)捕捉 https 协议的数据包

通过命令:

nc  -l  127.0.0.1  8888  >  https.data

   
   

在浏览器中输入 url 地址 https://127.0.0.1:8888 ,即可获得浏览器发送给服务器的数据,内容如下:


   
   
  1. 00000000: 1603 0100 c 2ae 0100 00c 2 aa 03 0328 6fc 2 .............(o.
  2. 00000010: a 227 0f 31 c 392 c 388 c 392 42c 2 8dc 2 9dc 2 .'. 1......B.....
  3. 00000020: 8275 c 297 2324 c 38f 484d 75c 2 8b 23 5bc 2 .u..#$..HMu..#[.
  4. 00000030: aac 3 98c 3 a 17c 0d 70 6cc 2 be 00 001c 2a 2a .....|.pl.....**
  5. 00000040: c 380 2bc 3 802f c 380 2cc 3 8030 c 38c c 2a 9 ..+../..,.. 0....
  6. 00000050: c 38c c 2a 8 c 380 13c 3 8014 00c 2 9c 00 c 29d ................
  7. 00000060: 002f 0035 000a 0100 0065 c 2aa c 2aa 0000 ./. 5.....e......
  8. 00000070: c 3bf 0100 0100 0017 0000 0023 0000 000d ...........#....
  9. 00000080: 0014 0012 0403 0804 0401 0503 0805 0501 ................
  10. 00000090: 0806 0601 0201 0005 0005 0100 0000 0000 ................
  11. 000000a0: 1200 0000 1000 0e 00 0c 02 6832 0868 7474 ..........h 2.htt
  12. 000000b0: 702f 312e 3175 5000 0000 0b 00 0201 0000 p/ 1. 1uP.........
  13. 000000c0: 0a 00 0a 00 086a 6a 00 1d 00 1700 182a 2a 00 .....jj......**.
  14. 000000d0: 0100 0a ...

可见,https协议的头信息是二进制数据流而非文本;

本文只对 http1.1 协议进行分析;

(3)分析 http Get请求数据包

浏览器发送到服务器端的请求数据为:


   
   
  1. GET / HTTP/ 1. 1
  2. Host: 127.0.0.1:8888
  3. Connection: keep-alive
  4. Upgrade-Insecure-Requests: 1
  5. User-Agent: Mozilla/ 5. 0 (X 11; Linux x 86_ 64) AppleWebKit/ 537. 36 (KHTML, like Gecko) Chrome/ 59. 0. 3071. 115 Safari/ 537. 36
  6. Accept: text/html,application/xhtml+xml,application/xml;q= 0. 9,image/webp,image/apng,*/*;q= 0. 8
  7. Accept-Encoding: gzip, deflate, br
  8. Accept-Language: zh-CN,zh;q= 0. 8,la;q= 0. 6,da;q= 0. 4

对于HTTP报文来说,第一行为报文的起始行,格式为

<method> <request-URL> <version>

   
   

每个字段用空格分隔;

在该例子中, method 为 GET ,request-URL 为 / ,version 为 HTTP/1.1

在这里,因为我们只是在浏览器中输入一个ip地址及端口号,默认的请求资源为 /

如果在浏览器中输入 http://127.0.0.1:8888/xxx/yy?name=abc&age=23 则 request-URL 的值将是 * /xxx/yy?name=abc&age=23 * ;

(4)捕获 http Post 请求数据包

下面我们来捕获以下Post的请求包,看看其与Get请求包的不同;

首先,我们创建一个html文件,文件地址为 :
/home/hbfeng/Code/Year2017/Mon07/Day19/x.html

文件内容为:


   
   
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Document </title>
  6. </head>
  7. <body>
  8. <form action="http://127.0.0.1:8888" method="POST">
  9. color: <input type="text" name="color">
  10. <input type="submit" value="提交" />
  11. </form>
  12. </body>
  13. </html>

之后,开启服务器 :

nc  -l  127.0.0.1  8888  >  post.dat

   
   

在浏览器中输入 :
file:///home/hbfeng/Code/Year2017/Mon07/Day19/x.html

可以出现如下页面,在文本框中填入内容,点击 提交 即可获得一个Post数据包:

POST数据获取

Post数据包的内容如下:


   
   
  1. POST / HTTP/ 1. 1
  2. Host: 127.0.0.1:8888
  3. Connection: keep-alive
  4. Content-Length: 12
  5. Cache-Control: max-age= 0
  6. Origin: null
  7. Upgrade-Insecure-Requests: 1
  8. User-Agent: Mozilla/ 5. 0 (X 11; Linux x 86_ 64) AppleWebKit/ 537. 36 (KHTML, like Gecko) Chrome/ 59. 0. 3071. 115 Safari/ 537. 36
  9. Content-Type: application/x-www-form-urlencoded
  10. Accept: text/html,application/xhtml+xml,application/xml;q= 0. 9,image/webp,image/apng,*/*;q= 0. 8
  11. Accept-Encoding: gzip, deflate, br
  12. Accept-Language: zh-CN,zh;q= 0. 8,la;q= 0. 6,da;q= 0. 4
  13. color=yellow

与Get请求的数据相比,Post数据包多出了以下我们后续编写代码时需要使用的内容:

  • Content-Length: 12 : 表示HTTP正文的大小。POST请求将数据以URL编码的形式放在HTTP正文中,字段形式为 fieldname=value,用&分隔每个字段;

  • HTTP信息头与HTTP正文之间有一行空行;

  • HTTP中有表单内容 color=yellow ,正好等于 Content-Length 的长度;

服务器的工作流程

知道了浏览器给我们发送的数据格式以后,我们的http服务器就可以将数据包进行解析,并动态生成页面发送给浏览器;

服务器的大致工作流程如下图所示:

tinyhttpd的工作流程

反馈给客户端的数据格式

知道了服务器的运行流程,我们需要知道浏览器希望从服务器端得到什么格式的数据;

服务器按照HTTP协议返回数据给客户端,如响应码为400,返回的内容为:


   
   
  1. HTTP/1.0 400 BAD REQUEST
  2. Content-type: text/html
  3. <!DOCTYPE>
  4. <html>
  5. <!-- html内容 -->
  6. ... ...
  7. </html>

每一行最后都跟 \r\n ,表示一行的结束;

第一行的3个参数用空格隔开,第一个参数说明服务器所使用的http协议为 HTTP/1.0 ,第二个参数是一个返回码,第三个参数是对返回码的解释;

第二行声明的是http正文内容的类型,text/html 表示正文是一个html文件的内容,浏览器将其解释并显示,如果是 text/plain 表示正文是纯文本,浏览器直接将内容显示,不需要解释、渲染等操作;

之后的空行表示http信息头结束;

空行之后的内容即为http正文;

动态生成Web页面技术

CGI (Common Gateway Interface,通用网关接口) :一种重要的互联网技术,是指根据浏览器发送过来的请求,服务器执行一定的动作,如数据库查询、系统信息查询等,甚至可以让服务器删除某些文件,之后生成对应的内容返回给浏览器以显示执行结果;

可以将CGI理解为通过浏览器就可以让服务器执行某些在服务器端已经定义好的功能,实现远程调用

CGI是这种技术的定义,而其实现方式多种多样,如 Perl 是一个广泛被用来编写CGI程序的语言,另外,像 PythonRubyC/C++PHP 等也可以实现CGI,甚至是 Shell脚本 文件也能胜任该任务;

CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。

在下一篇中,我们就来根据上面的流程图来实现一个小型的http服务器;

参考

HTTP协议入门

关于HTTP协议,一篇就够了

HTTP/1.1 Header Field Definitions

通用网关接口

完!



作者:FoolishFlyFox
链接:https://www.jianshu.com/p/51e2c2c831f4
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值