• 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 <name=content> 提交表单

    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: <a href="http://www.google.cn/">http://www.google.cn/</a>  
    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: <a href="http://www.google.cn">www.google.cn</a>  
    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响应的格式于请求的格式很类似:

    <status-line>
    <headers>
    <blank line>
    [<response-body>]

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

    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 <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    extern char** environ;
     
    int main()
    {
        // http协议的规定
        // 重定向
        printf("Content-type:text/html
    
    ");
     
        // 输出环境变量
        for(int i=0; ;++i)
        {
            if(environ[i])
            {
    //            printf("%s<br>
    ", 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<br>
    ");
        }
        else
        {
            printf("Login error<br>
    ");
        }
    }

    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 <stdio.h>
    #include <curl/curl.h>
    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的反向代理实现负载均衡。

  • 相关阅读:
    javascript 中检测数据类型的方法
    javascript 中的类数组和数组
    html5 构造网页的新方式
    关于 jQuery 中的 $.data() 方法和 jQuery 对象上的data 方法
    基于北洋PT站表结构分析以及ORM重写
    面试题准备
    sqlalchemy 外键
    sqlalchemy
    ansible roles
    ansible
  • 原文地址:https://www.cnblogs.com/w-x-me/p/6414562.html
Copyright © 2020-2023  润新知