一、什么是HTTP协议:
HTTP(HyperText Transfer Protocol),即超文本传输协议。主要用于互联网之间的网络传输,在网络模型中属于应用层的。所有的浏览器到WWW文件都必须遵守这个标准。
二、HTTP协议解析:
HTTP协议主要是服务器和客户端之间通信的,他们通信主要有两种协议:即请求协议和响应协议
1、请求协议:
请求协议主要是由浏览器发送给客户端的,而客户端会根据这个协议来识别浏览器发送过来的内容,然后再进行响应。
协议格式:
请求首行(New Request):
请求头信息(Request Headers):
空行
请求体(Request Body):
HTTP协议中的请求有很多种,但是常见的主要有两种,下面我们主要解析一下这两种:
1)GET:
下面是一个使用Mozilla firefox进行的一个GET路由器的首页192.168.1.1的示例:
GET: / HTTP/1.1
Host: 192.168.1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Cookie: username=%%
Connection: keep-alive
Upgrade-Insecure-Requests: 1
根据这个示例,我们来做一下解析:
GET: / HTTP/1.1
这个就是首行,/ 代表我们请求的路径,而HTTP/1.1则是我们使用的HTTP协议的版本号。这里注意,我么的路径为 / 根路径,但是并不是所有的请求都是根路径的。这个可以用Wiresherk抓包查看。
Host: 192.168.1.1
这个是我们请求的主机名
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
这个是与我们的浏览器和操作系统有关的额信息,有些网站显示的用户系统和浏览器版本信息就是从这里得到的。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
这个告诉了服务器,我们当前的客户端可以接收的文档类型的信息。而这里包含了*/*其实就是什么都可以接收的意思。
Accept-Encoding: gzip, deflate
这个告诉了服务器客户端支持的编码格式
Cookie: username=%%
如果不是第一次访问的话,那么请求就会将上一次服务器相应的Cookie在请求中一并发送过去。
Connection: keep-alive
这里是说客户端支持的连接方式,保持一段时间,默认是3000ms
2)POST:
下面的是一段我们登陆路由器的POST的信息:
POST /login.cgi HTTP/1.1
Host: 192.168.1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.1/
Content-Type: application/x-www-form-urlencoded
Content-Length: 82
Cookie: username=%%
Connection: keep-alive
Upgrade-Insecure-Requests: 1
username=admin&password=admin&selectLanguage=CH&LANGUAGE=&OKBTN=%E7%99%BB%E5%BD%95
我们简单分析一下这段信息的内容:
POST /login.cgi HTTP/1.1
和GET一样,包含路径和协议版本号
Host: 192.168.1.1
主机地址
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
用户主机和浏览器信息
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
支持的文档类型
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
当前客户端支持的语言,可以在浏览器的工具选型中设置
Accept-Encoding: gzip, deflate
客户端支持的编码方式
Referer: http://192.168.1.1/
请求来自哪个页面,如果是在百度上点击的连接来到了这里,那么就会显示百度的网址。这里因为我们是在192.168.1.1这个页面登陆的,因此这里显示的是这个地址。
Content-Type: application/x-www-form-urlencoded
表单的数据类型,说明会使用url编码,url编码数据都是以"%"为前缀,后面跟随两位16进制
Content-Length: 82
请求体的长度,这里是82个字节
Cookie: username=%%
上次链接时cookie的信息
Connection: keep-alive
连接状态
空行
username=admin&password=admin&selectLanguage=CH&LANGUAGE=&OKBTN=%E7%99%BB%E5%BD%95
本次POST请求的请求体,注意,GET请求中没有请求体。这里的请求体中包含了我们路由器的账号和密码信息!
3)GET和POST的区别:
1>GET是从服务器上获取数据,而POST是将数据上传至服务器。
2>get是把参数数据队列加到提交表单的ACTION属性所指的URL中。值和表单中相应的字段一一对应,在URL中可以看到。post是通过HTTP 的post机制,将表单中各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程
3>对于get方式,服务器端用requestQueryString获取变量的值。对于post方式服务器用Request.form获取提交数据
4>get传输的数据量较小,不能大于2kb,post传输的数据量比较大,一般默认为不受限制。但是理论上,IIS4中最大量为80kb,IIS5中最大量为100kb
5>get安全性非常低,post安全性较高。但是执行效率却比post方法好
2、响应协议:
响应协议是我们每次浏览器去向服务器发送一个请求之后,服务器给我们的响应,代表服务器收到了这次的请求。
相应协议:
响应首行():
响应头信息:
空行
响应体:
下面我们以一个正常的响应作为介绍:
HTTP/1.1 200 OK
Date: Mon, 08 Jan 2018 07:45:43 GMT
Server: Apache-Coyote/1.1
Content-Length: 0
Connection: keep-alive
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
…………………….
</form>
</body>
</html>
响应解析:
HTTP/1.1 200 OK
响应的协议为HTTP1.1状态码为200表示请求成功,OK是对状态码的解释
Date: Mon, 08 Jan 2018 07:45:43 GMT
相应的时间
Server: Apache-Coyote/1.1
服务器的版本信息
Content-Length: 0
相应体
Connection: keep-alive
连接状态
空行
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
…………………….
</form>
</body>
</html>
响应体,包含了一个新的网页的HTML信息。
其他响应头:
Last-Modified:最后修改时间
If-Modified-Since:上次请求的index.html的最后修改时间还给服务器
Expires:-1
Cache-Control:no-cache
Pragma:no-cache
告诉浏览器不要缓存的响应头:
Refresh:3 :url = http://xxxx.cn
自动刷新相应头,浏览器会在3秒之后跳转到http链接
三、HTTP响应首行返回值表:
1、大致含义:
1xx:保留
2xx:表示请求成功的接收
3xx:为完成求求客户需进一步细化请求
4xx:客户错误
5xx:服务器错误
2、具体含义:
响应码 | 状态说明 |
100 |
|
101 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
400 |
|
401 |
|
402 |
|
403 |
|
|
|
四、HTTP1.0和HTTP1.1的区别:
1、HTTP1.0协议:
1)会话建立方式:
1>建立连接(TCP/IP,底层)。
2>发出请求信息(client端发送HTTP头和内容)。
3>回送响应(server端返回HTTP响应首行HTTP版本和返回码)。
4>关掉链接(主动关闭TCP/IP底层连接)。
2、HTTP1.1协议:
1)会话建立:
1>建立连接
2>发送请求信息
3>回送响应
4>发送请求(如果包含连接关闭请求)
5>回送响应(响应连接关闭请求)
6>关闭连接
3、两者差别:
1)HTTP1.1支持长连接,HTTP1.0不支持长连接。
但HTTP1.1的长连接需要增加请求头的帮助来实现。例如:
client -> server : header Connection:Keep-Alive
如果server的回复中也包含了相应的响应头,则这个连接就是长连接。
server -> client:Connect: Keep-Alive
如果server回复的是: Connect: Close则这个连接将被关闭。
HTTP1.1 协议规定的默认连接类型是长连接。长连接的建立有利于节约系统的带块资源,避免每次连接都要通过TCP连接的重新握手和挥手。
2)HTTP1.1中增加了host字段,HTTP1.0中默认每个服务器只有唯一的IP地址。
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。
HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。此外,服务器应该接受以绝对路径标记的资源请求。
3)HTTP1.1增加了100的返回码,以节约带块资源:
HTTP/1.1加入了一个新的状态码100(Continue)。客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码401(Unauthorized);如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。100 (Continue) 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。
4)HTTP/1.1中引入了Chunked transfer-coding来解决上面这个问题,发送方将消息分割成若干个任意大小的数据块,每个数据块在发送时都会附上块的长度,最后用一个零长度的块作为消息结束的标志。这种方法允许发送方只缓冲消息的一个片段,避免缓冲整个消息带来的过载。
5)HTTP/1.1在1.0的基础上加入了一些cache的新特性,当缓存对象的Age超过Expire时变为stale对象,cache不需要直接抛弃stale对象,而是与源服务器进行重新激活(revalidation)
五、HTTP和HTTPS的区别:
1)
六、在Linux中利用socket来模拟HTTP操作网页的方法:
1)不同的协议版本因为连接方式以及消息头的区别会导致我们的方法可能会有差别,但是总体步骤基本一致。
2)这里是我们我使用linux模拟登陆wifi板子的HTTP1.0协议(这里由于我们的wifi板子只支持1.0的协议,因此每发送一次post就要重新socket一次)方法socket代码:
http.h
#ifndef _HTTP_RX_H #define _HTTP_RX_H #define HTTP_ON 1 #define HTTP_OFF 0 int setWifiOnOrOff(char state);
#endif |
http.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include "http_rx.h"
#define DEST_PORT 80 #define DEST_IP_ADDR "192.168.1.20" //#define DEST_IP_BY_NAME "192.168.1.21" #define SEND_NUM 2 static int sock_fd; static struct sockaddr_in addr_serv;
static void http_process_info(int state) { int send_num = 0; char recv_buf [1024*2] = {0}; char sum_str[SEND_NUM][1024*2] = {0};
while (1) { #if 1 //login strcat(sum_str[0], "POST /cgi-bin/login HTTP/1.1\r\n"); strcat(sum_str[0], "User-Agent: curl/7.35.0\r\n"); strcat(sum_str[0], "Host: 192.168.1.20\r\n"); strcat(sum_str[0], "Accept: */*\r\n"); strcat(sum_str[0], "Content-Length: 45\r\n"); //strcat(sum_str[0], "Content-Length: 70\r\n"); strcat(sum_str[0], "Content-Type: application/x-www-form-urlencoded\r\n"); strcat(sum_str[0], "Connection: keep-alive\r\n"); strcat(sum_str[0], "\r\n");
strcat(sum_str[0], "USER_NAME=61646D696E&USER_PASSWORD=61646D696E\r\n"); //strcat(sum_str[0], "WIFI0_WLAN0_SSID=6839\r\n"); strcat(sum_str[0], "\r\n\r\n"); #endif #if 1 //change Http wifi on if (HTTP_ON == state) { strcat(sum_str[1], "POST /cgi-bin/wirelessbasic HTTP/1.1\r\n"); strcat(sum_str[1], "User-Agent: curl/7.35.0\r\n"); strcat(sum_str[1], "Host: 192.168.1.20\r\n"); strcat(sum_str[1], "Accept: */*\r\n"); //strcat(sum_str[1], "Content-Length: 25\r\n"); strcat(sum_str[1], "Content-Length: 21\r\n"); strcat(sum_str[1], "Content-Type: application/x-www-form-urlencoded\r\n"); strcat(sum_str[1], "Connection: keep-alive\r\n"); //strcat(sum_str[1], "Expect: 100-continue\r\n"); strcat(sum_str[1], "\r\n");
strcat(sum_str[1], "WIFI0_WLAN0_SSID=6831\r\n"); //strcat(sum_str[1], "WIFI0_WLAN0_HIDDEN_SSID=0\r\n"); strcat(sum_str[1], "\r\n\r\n"); } #endif #if 1 //change Http wifi off if (HTTP_OFF == state) { strcat(sum_str[1], "POST /cgi-bin/wirelessbasic HTTP/1.1\r\n"); strcat(sum_str[1], "User-Agent: curl/7.35.0\r\n"); strcat(sum_str[1], "Host: 192.168.1.20\r\n"); strcat(sum_str[1], "Accept: */*\r\n"); //strcat(sum_str[1], "Content-Length: 25\r\n"); strcat(sum_str[1], "Content-Length: 21\r\n"); strcat(sum_str[1], "Content-Type: application/x-www-form-urlencoded\r\n"); strcat(sum_str[1], "Connection: keep-alive\r\n"); //strcat(sum_str[1], "Expect: 100-continue\r\n"); strcat(sum_str[1], "\r\n");
strcat(sum_str[1], "WIFI0_WLAN0_SSID=6839\r\n"); //strcat(sum_str[1], "WIFI0_WLAN0_HIDDEN_SSID=0\r\n"); strcat(sum_str[1], "\r\n\r\n"); } #endif int i = 0; for (i = 0; i<SEND_NUM; i++) { Resocket: sock_fd=socket(AF_INET, SOCK_STREAM, 0); if (sock_fd < 0) { perror("sock error"); goto Resocket; //exit(1); } else { printf("sock successful"); } if (connect(sock_fd, (struct sockaddr*)(&addr_serv), sizeof(addr_serv)) < 0) { perror("connect error\n"); goto Resocket; //exit(1); } else { printf("connect successful\n"); } Resend: printf("i = %d \n", i); printf("begin send\n"); printf("sum_str[%d] = %ld \n", i, strlen(sum_str[i])); send_num = send(sock_fd, sum_str[i],strlen(sum_str[i]),0); if (send_num < 0) { perror("send"); usleep(1000); goto Resend; //exit(1); } else { printf("send successful\n"); printf("send len = %ld \n", strlen(sum_str[i])); printf("send : \n%s\n",sum_str[i]); printf("begin recv:\n"); bzero(recv_buf, sizeof(recv_buf)); int recv_num = recv(sock_fd,recv_buf,sizeof(recv_buf),0); if(recv_num < 0) { perror("recv"); usleep(1000); //goto Resend; exit(1); } else { printf("recv successful\n"); printf("recv len = %ld \n", strlen(recv_buf)); printf("recv : \n%s\r\n\r\n",recv_buf);
if (recv_buf[9] != '2') { printf("recv_buf[9] = %c \n", recv_buf[9]); printf("HTTP Request Failed \n"); goto Resend; } usleep(1000); close(sock_fd); } } } sleep(1); break; } }
int setWifiOnOrOff(char state) { char WifiStatues = state; memset(&addr_serv, 0, sizeof(addr_serv)); addr_serv.sin_family = AF_INET; addr_serv.sin_port = htons(DEST_PORT); addr_serv.sin_addr.s_addr = inet_addr(DEST_IP_ADDR); /* sock_fd=socket(AF_INET, SOCK_STREAM, 0); if (sock_fd < 0) { perror("sock error"); goto Resocket; //exit(1); } else { printf("sock successful"); } struct hostent* hostInfo = gethostbyname(DEST_IP_BY_NAME); if(NULL == hostInfo){ printf("hostInfo is null\n"); return -6; }
printf("Ip address = %s \n",inet_ntoa(*((struct in_addr*)hostInfo->h_addr))); //memcpy(&addr_serv.sin_addr, &(*hostInfo->h_addr_list[0]), hostInfo->h_length); if (connect(sock_fd, (struct sockaddr*)(&addr_serv), sizeof(addr_serv)) < 0) { perror("connect error\n"); //goto Resocket; //exit(1); } else { printf("connect successful\n"); } */ http_process_info(WifiStatues); return 0; }
/* int main() { setWifiOnOrOff(ON); } */ |