zoukankan      html  css  js  c++  java
  • HTTP传输二进制初探

    【转】HTTP传输二进制初探

    http://www.51testing.com/?uid-390472-action-viewspace-itemid-233993

    【转】HTTP传输二进制初探

    上一篇 / 下一篇  2011-04-11 04:34:49 / 个人分类:知识

    从第一次接触http协议的时候,不知是怎么回事,形成了这么一个错误的观点,认为http协议是个纯ASCII字符协议,也就是说在http流里是看不到二进制流的0x00值的。其实答案是:http协议里的content可以是纯二进制流。
     
    http://my.chinaunix.net/space.php?uid=22568683&do=blog&id=84701

    关于HTTP传输ASCII文本内容的过程相信大家都应该容易理解,因为HTTP请求头和响应头都是以ASCII文本方式传输的。而对于HTTP传输二进制流的相关细节,其实没有我想象中的那么复杂,以前学习POP3和SMTP(这两个都是邮件传输协议)的时候,知道他们都只能传输ASCII文本,如果要在邮件中加入附件,如一张图片(图片文件就是二进制文件)那就得先对图片文件转码,即将邮件协议不能传输的二进制数据流转换成可被邮件协议传输的ASCII数据流,其中用的最多的转换就是BASE64编码转换。其实BASE64编码转换也同样适合于HTTP协议,只有你在转换后将HTTP响应头中的Transfer-Encoding设置为base64,当然如果客服端浏览器不支持base64编码那这种转换也是徒劳的,不过幸好现在几乎所有浏览器都支持BASE64(你能用浏览器查看邮件中的附件就是证据)。

    不过话又回来,既然HTTP能够直接支持二进制的数据流传输,那我们又何必绕着弯子,走冤枉路呢?

    如果你和一样,也对HTTP能直接传输二进制感到疑问,那么下面的内容会很对你胃口。

     我们以一张图片的传输来说明这个问题:

     http://gimg.baidu.com/img/gs.gif    这是百度主页上一张非常小的图片的链接地址 即右侧
    图片  
    我们用到的工具有:

    Firefox  浏览器

    Firebug  一个非常不错的web调试器,Firefox插件

    Ethereal  网络抓包工具

     如果你对上面三个工具不是很了解,建议你先去google一下。然后再来阅读。

     下面是我们在Firefox地址栏里面输入http://gimg.baidu.com/img/gs.gif  回车后,Firefox默默地为我们做的事情。关于HTTP通行的细节请参阅我以前的文章

    http://blog.chinaunix.net/u3/104217/showart.php?id=2075210

    http://p.99081.com/unix/http_protocol_summary.html

    GET  /gs.gif  HTTP/1.1

    Host: gimg.baidu.com

    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Accept-Language: zh-cn,zh;q=0.5

    Accept-Encoding: gzip,deflate

    Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7

    Keep-Alive: 300

    Connection: keep-alive

     当Firefox发出该请求后,服务器接收并分析该请求,经分析后得出客服端浏览器请求的文件(或者说页面)为/img/gs.gif (第一个斜杆代表服务器根目录),于是服务器(百度的HTTP服务器为Apache)从服务器主机的硬盘(或者直接从内存的缓冲区)中读出该图片(注意哦,直接读二进制流),并将其拼接到HTTP响应头后,然后把这片数据(我指的是HTTP响应头加上图片的二进制数据)拷贝到TCP的发送缓冲区(也就是调用send函数)。

    下面即位客服端收到的从服务器端发来的数据流:


    我们还是先来分析一下响应头

     HTTP/1.1 200 OK

    Date: Tue, 27 Oct 2009 13:43:15 GMT

    Server: Apache

    Last-Modified: Fri, 11 Aug 2006 04:20:15 GMT

    Accept-Ranges: bytes

    Content-Length: 91

    Cache-Control: max-age=315360000

    Expires: Fri, 25 Oct 2019 13:43:15 GMT

    Connection: close

    Content-Type: image/gif

     注意最后一个字段 Content-Type:image/gif 这说明传输的是一个image对象,该对象为gif格式。另外我们还有记下Content-length:91 这说明传输的数据(即gs.gif图片)的大小为91个字节,此外我们还发现响应头中并没有Transfer-Encoding这个字段,这说明传输的数据没有经过任何形式的编码转换,传输的就是源文件的内容。

    请认真查看上图中蓝底部分,在蓝底的最后那一行,有两个连续的 0d 0a 0d 0a ,这说明HTTP响应头已经结束,接下来的内容为传输的文件。好啦,那接下来当然是要分析传输的文件到底是啥东西了。请看下图

    图中蓝底的部分即为传输的数据流(即传输的文件),这些是什么东西,我也搞不懂,(估计只有搞图片压缩算法的人能够看得懂),不过没关系,我们可以先把图片保存在本地,然后用一个十六进制查看软件打开该图片,即可知道其中的奥秘。

    下面是用UltraEdit打开该图片后的截图

    请比较一下上面两张图片是不是有很多相同的地方呀,其实上面两张图中,第一张中的蓝底部分就是第二张中的数据。这下你应该明白了吧,其实HTTP传输的就是图片文件的二进制编码,Apache没有对二进制文件进行任何形式的编码转换。我们还可以计算一下这个图片的大小:16 * 5 + 11 = 91 (也就是 0x5a – 0x00 = 0x5a),正好和HTTP响应头中的Content-Length相等。

     如果没有UltraEdit等十六进制编辑器,我写了个简单的程序以供查看,下面是源码:

    check_hex.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define HEX_BUFFER    1024

    int main(int argc, char *argv[])
    {
        if(argc != 2)
        {
            printf("Usage: %s hex_file_path ", argv[0]);    
            exit(1);
        }
        
        FILE *fp = NULL;
        fp = fopen(argv[1], "rb");
        if(fp == NULL)
        {
            printf("open %s error. please check the path of file,and make sure you have permission to read it. ", argv[1]);
            exit(1);
        }    
        
        unsigned char hex_buf[HEX_BUFFER];
        memset(hex_buf, 0, HEX_BUFFER);
        
        int read_count = 0;
        
        read_count = fread(hex_buf, sizeof(unsigned char), HEX_BUFFER, fp);
        if(ferror(fp) != 0)
        {
            printf("read %s error. ", argv[1]);
            exit(1);
        }
        if(feof(fp) == 1)
        {
            printf("too large of %s, please enlarge macro HEX_BUFFER. ", argv[1]);    
            exit(1);
        }
        fclose(fp);
        
        int i = 0;
        int j = 0;
        printf(" addr 0 1 2 3 4 5 6 7 8 9 a b c d e f ");
        for( ; i < read_count; i++, j++)
        {
            if( (j % 16) == 0)
            {
                printf(" 0x%-4x ", j);
            }
            printf(

     
  • 相关阅读:
    web中的懒加载
    数据库表的关系
    struts2的MVC模式
    servlet与tomcat的关系
    servlet解析
    解决Mac外接显示器字体模糊的问题
    insmod: ERROR: could not insert module dm-snapshot.ko: Unknown symbol in module
    linux ssh tunnel
    Permission denied (publickey,gssapi-keyex,gssapi-with-mic).错误的解决
    Best practices for a new Go developer
  • 原文地址:https://www.cnblogs.com/zxtceq/p/7154488.html
Copyright © 2011-2022 走看看