HTTP协议


14.1 前言

在TCP/IP协议基础上,有很多应用层协议,支持各种网络应用,比如HTTP,SMTP,FTP等等。HTTP协议是最广泛的应用层协议

14.2 通信模型

客户端服务器发起TCP连接http请求报文http响应报文关闭连接客户端服务器
  • 支持客户/服务器模式。

  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。

  • 灵活:HTTP允许传输任意类型的数据对象。

  • 短连接:无连接的含义是限制每次连接只处理一个请求。

  • 无状态:HTTP协议是无状态协议。

14.3 地址(URL)

URL全称为Unique Resource Location,用来表示网络资源,可以理解为网络文件路径。URL的格式如下:

http://host[":"port][abs_path]

例如:

http://www.xueguoliang.cn:80/ds/index.html

URL的长度有限制,不同的服务器的限制值不太相同,但是不能无限长。以下博文有对RUL长度的一些叙述:
http://www.cnblogs.com/henryhappier/archive/2010/10/09/1846554.html

补充:HTTP协议的服务器和客户端

服务器

Http服务器/Web服务器:apache,nginx,iis(Windows平台)
客户端:浏览器

sudo apt-get install apache2

客户端

curl命令
curl是一个命令行数据传输工具,它支持很多协议,包括DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET and TFTP。这里使用curl来学习HTTP协议,因此重点说明HTTP相关的内容。

curl命令格式

curl [option] [URL...]

例如

curl http://www.baidu.com 该命令下载百度主页,并显示在标准输出。

选项[option]

选项长选项解释
-# --progress-bar 显示进度条
-d --data, --data-ascii, --data-binary post协议的数据
-F --form 提交表单

14.4 HTTP请求

HTTP请求由三部分组成,分别是:请求行、消息报头、请求正文


Snip20161103_2

14.4.1 请求方法

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如:GET /index.html HTTP/1.1

HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。

而常见的有如下几种:

  • GET

GET方法是HTTP协议中最常见的请求方式,当客户端需要从服务器中读取文档时,往往使用GET方法。GET方法要求服务器URL指定的文档内容。而内容部分,按照HTTP协议规定,放在响应报文的正文部分。

GET方法请求,没有请求正文内容,请求时的参数在URL中携带,由于URL被限制了长度,因此GET方法不适合用于上传数据。同时在浏览器中,通过GET方法来获取网页时,参数会显示在浏览器地址栏上,因此保密性很差。

GET / HTTP/1.1 Host: 192.168.11.80:9889 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive  
  • POST

POST方法运行客户端给服务器提供比较多的信息。POST方法将请求参数封装在HTTP请求数据中,而且长度没有限制,因为POST携带的数据,在HTTP的请求正文中。

POST /search HTTP/1.1   Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,application/msword, application/x-silverlight, application/x-shockwave-flash, */*   Referer: http://www.google.cn/">http://www.google.cn/   Accept-Language: zh-cn   Accept-Encoding: gzip, deflate   User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld)   Host: http://www.google.cn">www.google.cn   Connection: Keep-Alive   Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-FxlRugatx63JLv7CWMD6UB_O_r     hl=zh-CN&source=hp&q=domety  
  • HEAD

HEAD只返回信息头,不要求服务器返回URL所指资源内容。

14.4.2 消息报头

消息报头由关键字/值对组成,每行一对,关键字和值用:分割。
比如

Accept-Language: zh-cn   Accept-Encoding: gzip, deflate

14.4.3 空行

最后一个消息报头和正文之间,是一个空行。GET请求没有正文。

14.4.4 请求正文

请求正文在POST方法中使用。

14.5 HTTP响应

HTTP响应由三部分组成,分别是:状态行、消息报头、响应正文。HTTP响应的格式于请求的格式很类似:

[]

唯一的区别就是第一行的请求行,变成了状态行。状态行格式举例如下:

HTTP/1.1 200 OK

其中HTTP/1.1表示协议和版本信息,200表示服务器的响应码,OK表示状态码的描述,状态码由3位数组成,第一个数字表示了响应的类别,一共有5种类别。

  • 1xx:表示服务器正在处理

  • 2xx:成功,请求被正确的理解或者处理

  • 3xx:重定向

  • 4xx:客户端错误,客户端的请求,服务器无法理解。

  • 5xx:服务器错误,服务器未能实现的合法请求。

常见的状态码举例:

  • 200 OK:客户端请求成功

  • 400 Bad Request:请求报文有语法错误

  • 401 Unauthorized:未授权

  • 403 Forbidden:服务器拒绝服务

  • 404 Not Found:请求的资源不存在

  • 500 Internal Server Error:服务器内部错误

  • 503 Server Unavailable:服务器临时不能处理客户端请求(稍后可能可以)

响应报文例子:

HTTP/1.1 200 OK Date: Sat, 05 Nov 2016 08:14:20 GMT Server: Apache/2.4.7 (Ubuntu) Last-Modified: Sat, 05 Nov 2016 08:06:04 GMT ETag: "d-54089432494fd" Accept-Ranges: bytes Content-Length: 13 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html     this is xxx  

14.6 使用HTTP协议实现通信

服务器的开发不容易,尤其是开发高性能、稳定性好服务器,更加不容易,因此人们尝试更好简单的方式来开发软件。在服务器方面,使用WEB服务器,采用HTTP协议来代替底层的SOCKET,是常见的选择。采用HTTP协议更加除了能得到稳定的服务器支持外,更加可以兼容各种客户端(手机、PC、浏览器)等等。这样实现了一个服务器之后,多个客户端可以通用。

在开发应用程序时,客户端应用程序为了和WEB服务器通信,需要对请求报文进行打包成HTTP请求格式,而服务器响应了HTTP响应报文到客户端之后,也需要对响应报文进行解释。libcurl库,可以完成这些功能。

而Web服务器的选择是多样化的:Apache,nginx,libevent,tufao。一些使用C/C++开发的开源的Http服务器列表在这里:
http://www.oschina.net/project/tag/106?lang=21&os=0&sort=time

通信时,应用程序的通信报文目前采用的json格式较多,也有一些采用xml格式或者http协议规定的表单格式。

补充

客户端:浏览器
服务器:apache + c语言写的CGI程序
实现HTTP通信

CGI技术在早期解决动态网页,和网页跟服务器交互的问题。

  • apache2安装

  • 了解配置
    静态文档位置:/var/www/html
    apache可执行程序:/usr/lib/cgi-bin/

为了支持CGI,需要以下命令支持

cd /etc/apache2/mods-enable sudo ln -s ../mods-available/cgid.conf sudo ln -s ../mods-available/cgid.load sudo ln -s ../mods-available/cgi.load sudo /etc/init.d/apache2 restart

简单的CGI程序代码案例,编译出的可执行程序,需要在/usr/lib/cgi-bin目录下

#include 
#include <string.h>
#include 
 
extern char** environ;
 
int main()
{
    // http协议的规定
    // 重定向
    printf("Content-type:text/html\n\n");
 
    // 输出环境变量
    for(int i=0; ;++i)
    {
        if(environ[i])
        {
//            printf("%s
\n", environ[i]);
} else { break; } } // 获取环境变量QUERY_STRING的值,获取客户端参数 char* query_string = getenv("QUERY_STRING"); strtok(query_string, "=&"); char* username = strtok(NULL, "=&"); strtok(NULL, "=&"); char* password = strtok(NULL, "=&"); if(strcmp(username, "aa") == 0 && strcmp(password, "bb") == 0) { printf("Login success
\n
"); } else { printf("Login error
\n
"); } }

14.6.1 libcurl

libcurl 官网:https://curl.haxx.se/
libcurl也是curl工具使用的库

  • 下载源码

可以到官网下载,更方便的是到github上克隆代码

git clone https://github.com/curl/curl.git
  • 编译和安装

cd curl ./buildconf ./configure make sudo make install

安装完毕之后,头文件
/usr/local/include/curl
/usr/local/lib/libcurl.so
可执行命令
/usr/local/bin/curl

另外一种办法是:可以简单的执行
sudo apt-get install curl
安装curl命令,该命令将curl安装/usr/bin
sudo apt-get install libcurl4-openssl-dev
来安装libcurl,libcurl的安装路径
头文件:/usr/include/curl
库目录:/usr/lib

  • 使用

#include 
#include 
bool getUrl(char *filename)
{
    CURL *curl;
    CURLcode res;
    FILE *fp;
    if ((fp = fopen(filename, "w")) == NULL)  // 返回结果用文件存储
        return false;
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Accept: Agent-007");
    curl = curl_easy_init();    // 初始化
    if (curl)
    {
        //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
        curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的文件
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的文件
        res = curl_easy_perform(curl);   // 执行
        if (res != 0) {
 
            curl_slist_free_all(headers);
            curl_easy_cleanup(curl);
        }
        fclose(fp);
        return true;
    }
}
bool postUrl(char *filename)
{
    CURL *curl;
    CURLcode res;
    FILE *fp;
    if ((fp = fopen(filename, "w")) == NULL)
        return false;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86");    // 指定post内容
        //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
        curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi ");   // 指定url
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    fclose(fp);
    return true;
}
int main(void)
{
    getUrl("/tmp/get.html");
    postUrl("/tmp/post.html");
}

基本流程是:

  • 初始化CURL环境

  • 创建CURL对象

  • 设置CURL

  • 执行CURL

14.6.2 tufao

tufao是一个由QT编写的HTTP服务器。

tufao代替apache来实现http的通信。

安装tufao

  1. 获取代码

  2. 编译和安装

    • sudo apt-get install cmake qtsdk

    • 在tufao目录下创建build目录

    • cd build

    • cmake .. -DCMAKE_INSTALL_PREFIX=/usr

    • make

    • sudo make install

  3. 创建工程

    • 创建空的工程

    • 工程文件中增加CONFIG += TUFAO1 C++11

    • 增加一个类MyServer,一定是QObject派生类

    • 增加一个main.cpp实现main函数

    • 在MyServer的构造函数,创建Tufao::HttpServer对象server

    • 将server的信号requestReady和自己写的槽函数slotRequestReady连接

    • slotRequestReady函数中,实现http协议的响应报文。

14.6.3 libevent

略。

14.6.4 json

JSON是一种轻量级的数据交换格式,它简洁并且具有很好的可拓展性,容易阅读,也容易被机器解析,因此JSON成为当前最流行的数据交换格式。

JSON格式例子如下:

  • 用JSON来描述一个人的属性。

{     name: "tom",     age: 18,     man: true }
  • 嵌套对象:

{     name:"tom",     age:18,     mobile:{         branch: "apple",         version: 6     } }
  • 数组:

{     name:"tom",     age:18,     score:[         91,         100,         78,         85     ],     mobile: null   }
  • 根节点为数组

[     {         name:"tom",         age:18,         man:true     },     {         name:"rose",         age:18,         man:false     } ]

JSON由成对的key: value序列组成。key的类型永远是字符串,而value的类型可以是:

  • 数字:可表示整数或者浮点数

  • boolean:true或者false

  • null:null类型只有null一个值

  • 字符串:

  • 对象:使用{}定义

  • 数组:使用[]定义

各种语言都有JSON的生成和解析的库,C语言使用cJson,C++则更多的使用rapidJson。

14.6.5 xml

略。

14.7 HTTPS安全通信

一般网络通信是不安全的,因为有许多中间设备可能通过类似抓包的技术,可以获取报文,如果报文中携带一些敏感的信息,比如用户名和密码信息。

如果使用对称加密技术,密钥传输的安全性同样值得怀疑,而一旦密钥泄露,加密形同虚设。

非对称加密技术可以使用的加密密钥和解密密钥是不同的,加密密钥不能用于解密,这样加密密钥泄漏了,也不影响数据安全性。这样客户端可以安全得到加密密钥,而其他人得到加密密钥无法解密。

在HTTPS中,客户端随机生成对称的加密密钥,然后通过服务器给的非对称的加密密钥,加密对称的加密密钥,然后发给服务器,后续的通信使用对称机密。

HTTPS技术使得传输过程的安全无懈可击,但是如果认为任何带HTTPS的网站就是安全的那就错了,因为有些钓鱼网站,来骗取用户的敏感信息,因此需要第三方机构来监控提供服务的服务器的安全性。HTTPS证书需要花钱购买,也有一些机构颁发免费的HTTPS证书。

有些更加严格的HTTPS通信,要求客户端也必须有证书。因为别人可以得到加密密钥,就可以实行假冒行为。银行的客户端一般需要证书,而证书保存在银行配的u盘中。

在实际的编码中,HTTPS并不需要增加多少额外的工作,就可以实现HTTPS通信。

14.8 负载均衡

负载均衡技术,可以使得多个服务器分别负担繁重的用户请求。负载均衡可以通过许多技术来实现,比如DNS、NGINX反向代理、LVS、重定向等等。

目前比较流行的有NGINX的反向代理实现负载均衡。