大话HTTP协议

1. 霍霍哈嘿,这协议这协议!
一看到协议两个字,不要回顾上世纪和上上世纪的那段历史,也不要关注劳资纠纷和经济黑幕,虽然这些和协议有关,可不是今天的话题,今天动点真格的,谈技术层面的!

话说小甲一天遇到个外星人,外星人内急,对者小甲比手划脚打听W.C.的坐标,小甲不明白,以为要被绑架,撒丫子就跑,外星人一看,得,地球人有礼貌,还给带路,就紧跟小甲,这结果是@#$^&*(,...@@! 问题在哪,语言不通啊!

换个角度看这个问题,外星人叫Client,小甲外号叫Server,他们在Network上相遇,Client向Server发起请求,Server对Client作出响应。我们把这种第三类接触叫网络通讯,简称网通:-P

事后小甲和外星人在银河系法庭得到庭外和解,并且商定打听W.C,就摸鼻子,去餐厅就跺三下脚,去网吧就原地转两圈,去超市就卧倒10秒钟,诸如此类,等等等等,果然就再也没出过岔子,他们管这个叫协议。

HTTP就是在NetWork上碰面后约定的协议中的一种,大家通常管这种协议又叫做超文本传输协议。超文本是什么?网页呗。

这次外星人向小甲要网页,外星人说:

    GET /demo.htm HTTP/1.0/r/n
        Host: 192.168.1.1/r/n
        Referer: http://192.168.1.1/demo.htm/r/n
        User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)/r/n
        Accept: */*/r/n
        Connection: Keep-Alive/r/n
    /r/n

小甲就给了外星人demo.htm, 他是这样说的:

    HTTP/1.0 200 OK/r/n
    Content-Type: text/plain/r/n
    /r/n
    THIS IS A DEMO PAGE!

2. 先要在Network上相遇

继续小甲和外星人的第三类接触。一个问题,外星人是怎么来地球的?
步骤:
a. 购买飞船。
b. 坐上飞船,发射,咻~~~
c. 来到地球。

动作分解:
a. 购买并准备了叫作SOCKET的飞船。
b. 将导航图SOCKADDR_IN输入到飞船。
c. connect到时空隧道,好了,咻~~~,来到地球。

看看地球人小甲是怎样迎接外星人的。
步骤:
a. 建造停机坪
b. 做好对接准备
c. 等到外星人到来
d. 对接成功,欢迎外星人光临地球!

动作分解:
a. 建造SOCKET停机坪,与外星人飞船用的一样材质,这叫与宇宙接轨。
b. 还记得SOCKADDR_IN吧,这里要准备对接端口了,与外星人时空隧道的端口一样。
c. 停机坪SOCKET绑定(bind)好对接端口SOCKADDR_IN后,就等着(listen)吧.
d. 外星人来了,欢迎(accept)来到地球!
整个过程不复杂吧,来看看具体实施过程,这回从Server小甲说起:

    //1 .创建SOCKET
    SOCKET _socket = ::socket( PF_INET, SOCK_STREAM, 0 );
    if ( _socket == INVALID_SOCKET )
        return E_ERROR ;
    //2. 绑定端口
    SOCKADDR_IN sa;
    sa.sin_family = AF_INET;
    sa.sin_addr.S_un.S_addr = INADDR_ANY;
    sa.sin_port = htons( _port );
    if ( ::bind( _socket, (sockaddr*)&sa, sizeof(sa) )
        == INVALID_SOCKET ) {
        _socket = INVALID_SOCKET;
        return E_ERROR ;
    }
    //3. 监听
    if ( SOCKET_ERROR == ::listen( _socket, 5 ) ) {
        _socket = INVALID_SOCKET;
        return E_ERROR ;
    }
    //4. 接受
    SOCKET client = ::accept( _socket, NULL, NULL );

继续,再看看外星人Client的行动过程:

    //1. 创建SOCKET
    SOCKET _socket = ::socket( AF_INET, SOCK_STREAM, 0 );
    if ( _socket == INVALID_SOCKET )
        return E_ERROR ;
    //2. 设置连接参数
    SOCKADDR_IN sockAddr;  // server
    memset( &sockAddr, 0, sizeof(sockAddr) );
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = inet_addr( addr ); // addr是IP地址
    if ( sockAddr.sin_addr.s_addr == INADDR_NONE ) {
        struct hostent *ht = gethostbyname( addr );
        if ( ht == NULL ) {
            ::closesocket( _socket );
            return E_ERROR ;
        }
        sockAddr.sin_addr.s_addr = ((struct in_addr *)ht->h_addr)->addr;
    }
    sockAddr.sin_port = htons( (u_short)port ); //port是端口
    //3. 非Block方式
    //::ioctlsocket( _socket, FIONBIO, 1);
    //4. 连接
    ::connect( _socket, (struct sockaddr*)&sockAddr, sizeof(sockAddr) );

3. 来谈谈吧

   当Client和Server建立连接后,可以谈的话很多,通常根据谈话的内容类别分为HTTP、FTP、SMTP等众多协议,当然也可以根据需要规定自己的协议,比如我就规划过VSIP协议(Video Service Internet PROTOCOL)。

   HTTP协议的谈话过程比较简单,属于问答式的,Client按照一定格式向Server提问,Server回答Client的提问,并按照格式给出结果。

   a) 从最简单的谈话开始,获取页面
    举个例子说明.当TCP Client连接上WEB服务器后,发送如下字符串来获取demo.htm页面

       GET /demo.htm HTTP/1.0/r/n
        Host: 192.168.1.1/r/n
        Referer: http://192.168.1.1/demo.htm/r/n
        User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)/r/n
        Accept: */*/r/n
        Connection: Keep-Alive/r/n
    /r/n
解释一下吧。
-- GET:获取页面的动作方式;
--/demo.htm: 要获取的页面;
--HTTP/1.0:HTTP协议版本;
--Host:主机节点地址,也就是connect连接的地址;
--Referer:引用的页面全称,比如IE地址栏输入的URL;
--User-Agent:这个不用多解释了,HTML解析器的兼容类型,也就是html的接收者;
--Accept: 可以接收何种类型文档;
--Connection:网络连接后的行为方式,或Keep-Alive或Close;

    b) 进阶. 要我输入用户名密码怎么办?
是否遇到过这种情况, 请求的页面后返回"401 Unauthorized",用IE访问弹出输入用户名和密码的对话框,对这种情况怎样发送请求呢?

举例之.
在IE地址栏输入http://192.168.1.1/demo.htm,访问时弹出输入用户名和密码的对话框,这时输入用户名"root"密码"pass"(当然我知道用户名密码),OK,看到页面了; 也可这样,在IE地址栏输入http://root:pass@192.168.1.1/demo.htm,OK,直接看到页面!

老刀谈编程系列,深入浅出HTTP协议

    来看一下是怎么做到的! Client发送的内容有些变化!

    GET /demo.htm HTTP/1.0/r/n
        Host: 192.168.1.1/r/n
        Referer: http://192.168.1.1/demo.htm/r/n
        User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)/r/n
        Accept: */*/r/n
        Connection: Keep-Alive/r/n
    Authorization: Basic cm9vdDpwYXNz/r/n
        /r/n,

注意!关键有这么一项"Authorization", 那么用户名密码root:pass又在哪里呢? 注意到"Basic cm9vdDpwYXNz"了吗? 这里root:pass经过Base64编码,变成了"cm9vdDpwYXNz"!

    c) 提交表单
不用说了,提交表单地球人都知道是什么意思,如果不知道,下面也不用看了。
提交表单有两种方式:Get和Post。用Get提交的表单相当于在url上加参数,如http://192.168.1.1/demo.asp?usr=root&pwd=pass 用Post提交的表单则从URL上
看不到,保密性相对比较好。

看看是怎样工作的吧:
用Get方式:

    GET /demo.asp?usr=root&pwd=pass HTTP/1.0/r/n
        Host: 192.168.1.1/r/n
        Referer: http://192.168.1.1/demo.asp?usr=root&pwd=pass/r/n
        User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)/r/n
        Accept: */*/r/n
        Connection: Keep-Alive/r/n
    /r/n

相对的页面是:
<form action="http://192.168.1.1/demo.asp" method="GET">
<input type="text" size="10" value="root" name="usr">
<input type="password" size="10" value="pass" name="pwd">
</form>
ASP使用Request.QueryString的方式读取表单内容

再看一下Post:
    POST /demo.asp HTTP/1.0/r/n
        Host: 192.168.1.1/r/n
        Referer: http://192.168.1.1/demo.asp/r/n
        User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)/r/n
        Accept: */*/r/n
        Connection: Keep-Alive/r/n
    Content-Type: application/x-www-form-urlencoded/r/n
    Content-Length: 17/r/n
    usr=root&pwd=pass/r/n
    /r/n

相对的页面是:
<form action="http://192.168.1.1/demo.asp" method="POST">
<input type="text" size="10" value="root" name="usr">
<input type="password" size="10" value="pass" name="pwd">
</form>
ASP使用Request.Form的方式读取表单内容

另外,GET用于提交少量数据,而POST用于提交大量数据,如图片等,在接下来的编程系列中我来谈谈用C/C++代码HTTP上载图片到服务器。今天先到这里了,就到这里,休息,休息一下!

附base64编码算法:

void encode( char *in, char *out )
{
            int ilen = strlen(in);
            assert( ilen );

            int zlen = ( 3 - ilen % 3 ) % 3;

            int p1len = ilen + zlen;
            char *p1 = new char[p1len];
            memset( p1, 0, p1len );
            strncpy( p1, in, ilen );

            int p2len = p1len / 3 * 4;
            char *p2 = new char[ p2len + 1 ];

            // Loop 24 bit groups
            int bit[24];  // 0 / 1
            int j = 0;  // index of p2
            char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
            for ( int i = 0; i < p1len / 3; i ++ )
            {
                bit[ 0] = ( p1[i * 3    ] & 0x80 ) ? 1 : 0;
                bit[ 1] = ( p1[i * 3    ] & 0x40 ) ? 1 : 0;
                bit[ 2] = ( p1[i * 3    ] & 0x20 ) ? 1 : 0;
                bit[ 3] = ( p1[i * 3    ] & 0x10 ) ? 1 : 0;
                bit[ 4] = ( p1[i * 3    ] & 0x08 ) ? 1 : 0;
                bit[ 5] = ( p1[i * 3    ] & 0x04 ) ? 1 : 0;
                bit[ 6] = ( p1[i * 3    ] & 0x02 ) ? 1 : 0;
                bit[ 7] = ( p1[i * 3    ] & 0x01 ) ? 1 : 0;
                bit[ 8] = ( p1[i * 3 + 1] & 0x80 ) ? 1 : 0;
                bit[ 9] = ( p1[i * 3 + 1] & 0x40 ) ? 1 : 0;
                bit[10] = ( p1[i * 3 + 1] & 0x20 ) ? 1 : 0;
                bit[11] = ( p1[i * 3 + 1] & 0x10 ) ? 1 : 0;
                bit[12] = ( p1[i * 3 + 1] & 0x08 ) ? 1 : 0;
                bit[13] = ( p1[i * 3 + 1] & 0x04 ) ? 1 : 0;
                bit[14] = ( p1[i * 3 + 1] & 0x02 ) ? 1 : 0;
                bit[15] = ( p1[i * 3 + 1] & 0x01 ) ? 1 : 0;
                bit[16] = ( p1[i * 3 + 2] & 0x80 ) ? 1 : 0;
                bit[17] = ( p1[i * 3 + 2] & 0x40 ) ? 1 : 0;
                bit[18] = ( p1[i * 3 + 2] & 0x20 ) ? 1 : 0;
                bit[19] = ( p1[i * 3 + 2] & 0x10 ) ? 1 : 0;
                bit[20] = ( p1[i * 3 + 2] & 0x08 ) ? 1 : 0;
                bit[21] = ( p1[i * 3 + 2] & 0x04 ) ? 1 : 0;
                bit[22] = ( p1[i * 3 + 2] & 0x02 ) ? 1 : 0;
                bit[23] = ( p1[i * 3 + 2] & 0x01 ) ? 1 : 0;

                p2[j++] = base64[ bit[0] * 32 + bit[1] * 16 + bit[2] * 8
                    + bit[3] * 4 + bit[4] * 2 + bit[5] ];
                p2[j++] = base64[ bit[6] * 32 + bit[7] * 16 + bit[8] * 8
                    + bit[9] * 4 + bit[10] * 2 + bit[11] ];
                p2[j++] = base64[ bit[12] * 32 + bit[13] * 16 + bit[14] * 8
                    + bit[15] * 4 + bit[16] * 2 + bit[17] ];
                p2[j++] = base64[ bit[18] * 32 + bit[19] * 16 + bit[20] * 8
                    + bit[21] * 4 + bit[22] * 2 + bit[23] ];
            }

            p2[j] = '/0';
            for ( int n = 0; n < zlen; n ++ )
                p2[--j] = '=';

            strcpy(out, p2);
            delete [] p1;
            delete [] p2;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值