http协议的分析

一, http协议

http协议 - 应用层
    - 请求消息(Request)  -  浏览器给服务器发
        ○ 四部分: 请求行, 请求头, 空行, 请求数据
            § 请求行: 说明请求类型, 要访问的资源, 以及使用的http版本
            § 请求头: 说明服务器要使用的附加信息
            § 空行: 空行是必须要有的, 即使没有请求数据
            § 请求数据: 也叫主体, 可以添加任意的其他数据
        ○ 例:
        GET /3.txt HTTP/1.1
            /: 资源目录的根目录
            三部分内容由空格间隔
        Host: localhost:2222
        User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/201001    01 Firefox/24.0
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
        Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
        Accept-Encoding: gzip, deflate
        Connection: keep-alive
        If-Modified-Since: Fri, 18 Jul 2014 08:36:36 GMT
            请求头:由键值对构成的

        请求数据
                □ get: 肯定没有
                □ post : 有数据
        换行:\r\n
    - 响应消息(Response) - 服务器给浏览器发
        ○ 四部分: 状态行, 消息报头, 空行, 响应正文
            § 状态行: 包括http协议版本号, 状态码, 状态信息
            § 消息报头: 说明客户端要使用的一些附加信息
            § 空行: 空行是必须要有的
            § 响应正文: 服务器返回给客户端的文本信息
        ○ 例:
        HTTP/1.1 200 Ok
        Server: micro_httpd
        Date: Fri, 18 Jul 2014 14:34:26 GMT
        Content-Type: text/plain; charset=iso-8859-1 (必选项)
            告诉浏览器发送的数据是什么类型
        Content-Length: 32  
            发送的数据的长度
        Content-Language: zh-CN
        Last-Modified: Fri, 18 Jul 2014 08:36:36 GMT
        Connection: close

        #include <stdio.h>
        int main(void)
        {
            printf("hello world!\n");
            return 0;
        }

    - HTTP1.1的五种请求方法
        ○ GET     
            § 请求指定的页面信息,并返回实体主体。
        ○ POST     
            § 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
        ○ HEAD     
            § 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
        ○ PUT     
            § 从客户端向服务器传送的数据取代指定的文档的内容。
        ○ DELETE      
            § 请求服务器删除指定的页面。
        ○ CONNECT     
            § HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
        ○ OPTIONS 
            § 允许客户端查看服务器的性能。
        ○ TRACE     
            § 回显服务器收到的请求,主要用于测试或诊断。

    - HTTP常用状态码
    状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
        ○ 1xx:指示信息--表示请求已接收,继续处理
        ○ 2xx:成功--表示请求已被成功接收、理解、接受
        ○ 3xx:重定向--要完成请求必须进行更进一步的操作
        ○ 4xx:客户端错误--请求有语法错误或请求无法实现
        ○ 5xx:服务器端错误--服务器未能实现合法的请求

        ○ 常见状态码:
            § 200 OK                       客户端请求成功
            § 400 Bad Request       客户端请求有语法错误,不能被服务器所理解
            § 401 Unauthorized      请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 
            § 403 Forbidden            服务器收到请求,但是拒绝提供服务
            § 404 Not Found           请求资源不存在,eg:输入了错误的URL
            § 500 Internal Server Error     服务器发生不可预期的错误
            § 503 Server Unavailable        服务器当前不能处理客户端的请求,一段时间后可能恢复正常

二, http四种格式

    1. http请求协议, 请求 的时候使用的Post方法
    2. 概述
        ○  HTTP 请求分为四个部分:请求行、请求头、空行、主体
        ○ 协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。
        ○ 开发者完全可以自己决定消息主体的格式
        ○ 数据发送出去,还要服务端解析成功才有意义, 服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。
    3. 四种方式
        ○ application/x-www-form-urlencoded
            POST http://www.example.com HTTP/1.1
            Content-Type: application/x-www-form-urlencoded;charset=utf-8
            title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3
        ○ application/json
            POST http://www.example.com HTTP/1.1
            Content-Type: application/json;charset=utf-8
            {"title":"test","sub":[1,2,3]}
        ○ text/xml
            POST http://www.example.com HTTP/1.1
            Content-Type: text/xml
            <!--?xml version="1.0"?-->
            <methodcall>
                <methodname>examples.getStateName</methodname>
                <params>
                    <value><i4>41</i4></value>
                </params>
            </methodcall>
        ○ multipart/form-data
            ------WebKitFormBoundaryPpL3BfPQ4cHShsBz \r\n
            Content-Disposition: form-data; name="file"; filename="qw.png"
            Content-Type: image/png\r\n
            \r\n
             PNG
            ...................................................................................
            ...................................................................................
            ------WebKitFormBoundaryPpL3BfPQ4cHShsBz
            Content-Disposition: form-data; name="tailor"

            false
            ------WebKitFormBoundaryPpL3BfPQ4cHShsBz--
        ○ 解析数据块
            § 把绿色的数据取出来, 保存到文件中
            § 先读第一行 : 分界线 - 存储到内存
                □ 遇到第二个同样的分界线, 说明文件内容结束了.
                □ char *p = strstr(big, sub)
        ○ 注意事项:
            § boundary是一个字符串,用来切分数据
            § 在HTML协议中换行使用的是:"\r\n"
            § BODY里面的bounday比HEADER里面的前面都多了“--”




三, 解析http格式

#include "fcgi_config.h"
#include "fcgi_stdio.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "fdfs_api.h"
#include "deal_mysql.h"
#include "make_log.h"
#include "upload.h"

char* memstr(char* full_data, int full_data_len, char* substr)
{
    if (full_data == NULL || full_data_len <= 0 || substr == NULL)
    {
        return NULL;

    }

    if (*substr == '\0')
    {
        return NULL;

    }

    int sublen = strlen(substr);
    char* cur = full_data;
    int last_possible = full_data_len - sublen + 1;
    for (int i = 0; i < last_possible; i++)
    {
        if (*cur == *substr)
        {
            if (memcmp(cur, substr, sublen) == 0)
            {
                // found
                return cur;
            }

        }
        cur++;
    }
    return NULL;
}

int get_file_content(char* begin, char* end, int len, char* filename)
{
    char *p = NULL;
    // 取出并保存分界线字符串
    char boundary[256] = {0};
    // p 指向该行的\r字符
    p = strstr(begin, "\r\n");
    strncpy(boundary, begin, p-begin);
    // p指向下一行行首
    p += 2;
    // 剩余字符串长度
    len -= (p-begin);
    printf("boundary: %s", boundary);

    // 记录第二行行首位置
    begin = p;
    // content-disp, 保存到begin中
    p = strstr(begin, "\r\n");
    // 移动到下一行行首 - 第三行
    p += 2;
    // 剩余数据长度
    len -= (p-begin);
    // 将filename搞出来
    char* pt = strstr(begin, "filename=");
    pt += strlen("filename=");  // 此时pt指向双引号
    // 指针指向文件名的第一个字符
    pt ++;
    // 找到文件名的结尾字符
    char* q = strchr(pt, '"');
    // 保存文件名
    strncpy(filename, pt, q-pt);
    printf("<br>filename: %s<br>", filename);

    // content-type 第三行
    begin = p;
    p = strstr(begin, "\r\n");
    // 跳过\r\n和空行(\r\n)
    p += 4;
    // 剩余数据长度
    len -= (p-begin);

    // 文件内容正式开始 - 图片正文开始
    begin = p;
    // 从剩余数据块中找到结束分界线
    p = memstr(begin, len, boundary);
    if(p == NULL)
    {
        // 减去\r\n长度
        p = end - 2;
    }
    else
    {
        // 减去\r\n长度
        p -= 2;
    }

    // 将上传文件内容写入文件中
    int fd = open(filename, O_CREAT|O_WRONLY, 0664);
    write(fd, begin, p-begin);
    close(fd);

    return 0;
}

int store_data(char* filename, char* fileid)
{
    // 连接数据库
    MYSQL* conn = NULL;
    conn = msql_conn("root", "root", "test");
    if(conn == NULL)
    {
        LOG("upload_file", "mysql", "数据库连接失败!");
        return -1;
    }
    // 设置数据库编码,否则,中文插入乱码
    mysql_query(conn, "set names utf8");
    // 插入数据
    //插入
    char buf[1024];
    sprintf(buf, "insert into file (name, fileid) values ('%s', '%s')",
            filename, fileid);
    printf("<br> sql: %s<br>\n", buf);
    // 执行sql语句
    // 如果成功,它返回0。
    if ( mysql_query (conn, buf) != 0 ) 
    {
        LOG("upload_file", "mysql", "数据插入失败");
        return -1;
    }
    // 关闭数据库连接
    mysql_close(conn);
    return 0;
}

int main ()
{
    while (FCGI_Accept() >= 0)
    {
        int len = 0;
        // post数据的长度
        char *contentLength = getenv("CONTENT_LENGTH");
        printf("Content-type: text/html\r\n"
                               "\r\n");

        if (contentLength != NULL)
        {
            len = strtol(contentLength, NULL, 10);
        }

        if (len <= 0)
        {
            printf("No data from standard input.<p>\n");
        }
        else
        {
            int ch;
            char filename[128] = {0};
            // 根据post数据长度, 创建对应大小的数组
            char* file_data = (char*)malloc(len);
            char *begin, *end, *p;
            // 指针初始化
            end = NULL;
            begin = p = file_data;

            // 读post数据, 存储到file_data中
            for (int i = 0; i < len; i++)
            {
                if ((ch = getchar()) < 0)
                {
                    printf("Error: Not enough bytes received on standard input<p>\n");
                    break;
                }
                *p = ch;    // 赋值
                p++;
            }
            // end 指向文件结束位置
            end = p;

            // 得到文件內容
            get_file_content(begin, end, len, filename);

            // 文件上传到fastdfs
            char fileid[1024] = {0};
            fdfs_upload_file(filename, fileid);
            printf("<br>fileid: %s\n<br>", fileid);
            // qw.png - /group1/M00/00/00/xxxxxx.png

            // 将数据存储到数据库中
            store_data(filename, fileid);

            // 释放内存
            free(file_data);
            // 删除文件
            unlink(filename);
        }
    } /* while */

    return 0;
}
(1) HTTP超文本传输协议用于定义web页面(超文本)在网络上的交互方式的应用层协议,使用客户/服务器的工作方式。HTTP协议定义了web客户端(浏览器)如何向web站点请求web网页以及web服务器如何将web页传送给客户机。当用户请求一个页面时,浏览器会向web服务器发出对该页及其引用的相关对象的HTTP请求报文,服务器响应这些请求报文,生成HTTP响应报文,并将请求的对象附在HTTP响应报文后发送给客户端。 (2) 由于网页文档的传输需要可靠性的保证,所以HTTP协议使用TCP协议,TCP协议是一个面向连接的协议,在通信时需要建立连接,通信结束要释放连接,TCP建立连接时需要三次握手,提供可靠的数据传输,HTTP协议默认端口为80. (3) HTTP协议包括HTTP1.0:RFC1945和HTTP1.1:RFC 2068.HTTP1.1兼容HTTP1.0。HTTP1.0是采用非持续连接和非流水线作业方式,而HTTP1.1采用持续连接和流水线作业方式。 (4) Web缓存的进一步讨论,保证web缓存代理服务器缓存页面能够和web服务器的最新页面一致的方法。 (5) HTTP协议的无状态性:服务器无法通过HTTP协议记录用户的登录信息。使用Cookie保存信息。 (1) 打开wireshark开始抓包,然后上网至江西师大教务在线网(www.jxnu.edu.cn),连接后停止(stop)抓包。设置过滤规则:udp.port==53||tcp.port==80.(HTTP协议默认使用传输层TCP协议的80端口),考虑到我的电脑未能连网,所以选择老师抓好的包做实验分析。 (2) 分析HTTP协议报文。 (2) 第一阶段:TCP建立连接阶段。第三帧是web客户端(即浏览器,源IP地址为192.168.0.250)发给web服务器端(目标IP地址为218.65.113.46)的TCP连接的第一次握手(SYN)。第四帧是web服务器端与web客户端的TCP连接的第二次握手(SYN,ACK)。第五帧是浏览器与web服务器的TCP连接的第三次握手(ACK),此时TCP连接建立。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值