http协议
- URL
- urlencode编码 与 urldecode解码
- HTTP协议格式
- HTTP请求
- 请求首行
- 请求头部(Header)
- 空行
- 正文(Body)
- HTTP响应
- 响应首行
- HTTP请求方法
- HTTP状态码
- HTTP常见Header
HTTP
是典型的应用层协议,是目前最流行的通信协议,没有之一。
其衍生出了封装SSL
协议的“安全版”:HTTPS
协议,相对于传统的HTTP
协议更为安全,但也是“相对的安全”。
只要保证通信传输一端发送时构造的数据, 在另一端能够正确的进行解析, 就是可以的,这种约定就是 应用层协议。是应用程序之间的沟通,这其中也涉及序列化与反序列化。
序列化:将数据按照持久化存储或网络数据传输的格式进行排布。
反序列化:对数据以指定的协议进行解析。
虽然应用层协议是程序猿自定制的的。但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP
(超文本传输协议
)就是其中之一。
URL
平时我们俗称的 “网址” 其实就是 URL
(统一资源定位符号(Uniform Resource Locator
),其格式为:
urlencode编码 与 urldecode解码
像/
、?
、:
等这样的字符, 已经被url
当做特殊意义理解了. 因此这些字符不能随意出现。
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16
进制,然后从右到左,取4位(不足4位直接处理),每2
位做一
位,前面加上%
,编码成%XY
格式。
例如在百度上搜索C++
,其URL
为:
https://www.baidu.com/s?wd=C%2B%2B&rsv_spt=1…
其中的%2B%2B
就是转义字符,"%2B"
就是"+"
字符的转义字符。
网上编码解码的工具很多,这里简单介绍一个以便读者使用:【 http://tool.chinaz.com/Tools/urlencode.aspx 】
HTTP协议格式
通过分析数据包数据就可以明晰网络通信原理,这里我们使用fiddler
工具进行抓包,其界面如下,从中就可以获得HTTP
请求数据、响应数据等。
HTTP请求
请求格式为:请求首行、请求头部、空行、正文
//利用随意抓取的一个数据包举例:
GET http://neirong.funshion.com/airport/lua/fire.php?name=Fireman&cid=1001799 HTTP/1.1
Cache-Control: max-age=43200
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko Funshion/1.0.0.1
Host: neirong.funshion.com
Connection: Keep-Alive
Pragma: no-cacheA SSLv3-compatible ClientHello handshake was found. Fiddler extracted the parameters below.
Version: 3.3 (TLS/1.2)
Random: 18 75 2D C6 A0
请求首行
格式为:[方法]
+ [url]
+ [版本]
[GET]
[http://neirong.funshion.com/airport/lua/fire.php?name=Fireman&cid=1001799]
[HTTP/1.1]
请求头部(Header)
请求的属性, 冒号:
分割的键值对。每组属性之间使用\n
分隔;
Cache-Control: max-age=43200
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko Funshion/1.0.0.1
Host: neirong.funshion.com
...
空行
请求头部信息遇到空行表示Header
部分结束。(连续遇到2个\r\n
就认为是头部信息结束了)
正文(Body)
空行后面的内容都是Body
. 它允许为空字符串. 如果Body
存在, 则在Header
中会有一个Content-Length
属性来标识Body
的长度;
HTTP响应
响应首行
格式为: [版本号]
+ [状态码]
+ [状态码解释]
[HTTP/1.1] [200] [OK]
其他都与请求的元素相同,包含Header
、空行、Body
。
HTTP请求方法
方法 | 说明 | 支持版本 |
---|---|---|
GET | 获取资源 | 1.0/1.1 |
POST | 传输实体主体 | 1.0/1.1 |
PUT | 传输文件 | 1.0/1.1 |
HEAD | 获得报文头部 | 1.0/1.1 |
DELETE | 删除文件 | 1.0/1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开联系 | 1.0 |
其中最常用和常见的还是GET
和POST
方法。
HTTP状态码
/ | 类别 | 原因 |
---|---|---|
1xx | 信息性状态码 | 接收的请求正在处理 |
2xx | Success(成功 ) | 请求的数据处理完毕 |
3xx | Redirection(重定向 ) | 需要进行附加操作以完成请求 |
4xx | Client Error(客户端 错误) | 服务器无法处理请求 |
5xx | Server Error(服务器 错误) | 服务器处理请求出错 |
最常见的状态码, 比如 :
200
(OK)
404
(Not Found)
403
(Forbidden,没有权限)
404
(Not Found)
302
(Redirect, 重定向)
504
(Bad Gateway,坏的网关)
HTTP常见Header
Content-Type
: 数据类型(text
/html
等)Content-Length
:Body
的长度Host
: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;User-Agent
: 声明用户的操作系统和浏览器版本信息;referer
: 当前页面是从哪个页面跳转过来的;location
: 搭配3xx
状态码使用, 告诉客户端接下来要去哪里访问;Cookie
: 用于在客户端存储少量信息. 通常用于实现会话(session
)的功能
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Usage() {printf("usage: ./server [ip] [port]\n");
}
int main(int argc, char* argv[]) {if (argc != 3) {Usage();return 1;}int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0) {perror("socket");return 1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(argv[1]);addr.sin_port = htons(atoi(argv[2]));int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("bind");return 1;}ret = listen(fd, 10);if (ret < 0) {perror("listen");return 1;}for (;;) {struct sockaddr_in client_addr;socklen_t len;int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);if (client_fd < 0) {perror("accept");continue;}char input_buf[1024 * 10] = {0}; // 用一个足够大的缓冲区直接把数据读完.ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);if (read_size < 0) {return 1;}printf("[Request] %s", input_buf);char buf[1024] = {0};const char* hello = "<h1>hello world</h1>";sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);write(client_fd, buf, strlen(buf));}return 0;
}
编译, 启动服务. 在浏览器中输入 http://[ip]:[port]
, 就能看到显示的结果 "Hello World"
此处我们使用 9090
端口号启动了HTTP
服务器. 虽然HTTP
服务器一般使用80
端口,但这只是一个通用的习惯. 并不是说HTTP服务器就不能使用其他的端口号.