您的位置:首页 > 理论基础 > 计算机网络

http协议请求部首详解以及用c++写socket下载文件

2015-08-24 22:09 585 查看
本篇博文将介绍几个我认为比较有作用的请求部首

Connection ,这个部首常见的值是keep-alive 和close。close的意思是说,我发一次请求,你回复给我消息,那么咱俩就立刻断开,等我下次想请求数据时,我会再次发起tcp连接。而keep-alive的意思是说,这一次,咱俩好不容易建立起连接,不要那么快断开,多保持一段时间,我还有很多请求要发送呢。当你看到这篇文章时,浏览器里只有一个地址,但是你可不要以为浏览器只向服务器发送了一次请求就拿到了整个网页。这网页里还有许多其他的资源,比如css,js都是需要加载的,那么这些都需要浏览器向服务器发送请求。如果每一次请求完毕后都断开连接,那么下一次就需要重新发起连接。这样对双方来说都很糟糕,因为建立TCP连接是需要耗费资源的,因此就有了keep-alive,如果服务器支持这种方式,那么双方就可以建立起长连接

Referer, 网页里存在大量的连接,我们点击后就会跳转到别的网页,比如,你在A网页中点击了一个网页跳转到了B网页,那么这时,请求B网页的请求头部首中就会有Referer这个字段,这个字段里记录的是A网页的地址,说白了,就是告诉服务器,我们是从哪里跳转过来的。这个东西还是蛮有作用的,通过分析Referer,我们就能知道用户是经过什么样的途径来到我们的网站的,这对于电子商务网站来说有着很重要的数据挖掘价值。

Range,我们向服务器请求资源时,可以明确的告知,我们请求的是哪一部分,比如一个下载的文件有4M大小,那么浏览器可以发出4个请求,分别请求1M大小的数据,这时就需要告诉服务器,本次请求的资源时哪个范围内的,举例:Range: bytes = 500 -1500,这个部首的意思就是说,请求资源的第500到1500byte的内容。对于小文件来说,其实没有啥必要,但是对于很大的文件来说,这就显得非常有必要了,如果能分块请求,那么即便其中一块出了啥问题,其他的部分不需要重新下载的,只需要重新请求出问题的那块就可以了。

User-Agent,这个部首会提供一些关于客户端的信息,比如用的什么操作系统,用的什么浏览器,通过这个部首,理论上可以区分请求是来自于PC还是智能手机

OK,已经介绍了四个请求部首,下面说说如何用c++发起socket请求下载文件

首先,找到一个可用的下载资源 http://f1.market.xiaomi.com/download/AppStore/0831c047a8a8e
4cff181d1466c39f5d4145402b80/com.julanling.app.apk,

打开cmd命令窗口 ping f1.market.xiaomi.com ,这样就可以获得它的IP地址,我这里获得是106.38.211.3,不同的人可能获取的不一样,但是没关系,之所以会这样,是因为网站太大了,用的是服务器集群,所以DNS会解析出多个IP地址,但是每一个都可以使用。现在,我们已经知道了IP地址,端口默认80,也知道了host和 request-URL,那么我们就可以向服务器发送请求了,贴上代码

#include<iostream>
#include<WinSock2.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
//http://f1.market.xiaomi.com/download/AppStore/0831c047a8a8e4cff181d1466c39f5d4145402b80/com.julanling.app.apk
const int BuffSize = 1024;
void downLoad(){
WSADATA WsaData;
if (WSAStartup(MAKEWORD(2,2),&WsaData))
{
printf("初始化失败");
return ;
}

SOCKET sockeId;
SOCKADDR_IN addr;

if (-1 == (sockeId = socket(AF_INET, SOCK_STREAM, 0)))
{
printf("创建socket失败");
return;
}

addr.sin_addr.S_un.S_addr = inet_addr("106.38.211.3"); ///
addr.sin_family = AF_INET;
addr.sin_port = htons(80);

if (SOCKET_ERROR == connect(sockeId, (SOCKADDR * )&addr, sizeof(addr)))
{
printf("连接服务器失败");
closesocket(sockeId);
WSACleanup();
return ;
}

// 初始化请求的数据
char* pReqHead = new char[BuffSize];
pReqHead[0] = '\0';
// 请求行
strcat(pReqHead,"GET ");
strcat(pReqHead,"/download/AppStore/0831c047a8a8e4cff181d1466c39f5d4145402b80/com.julanling.app.apk");
strcat(pReqHead," HTTP/1.1\r\n");

//headers
strcat(pReqHead,"host:");
strcat(pReqHead,"f1.market.xiaomi.com");
strcat(pReqHead, "\r\nConnection: Close\r\n\r\n");
printf(pReqHead);

if (SOCKET_ERROR == send(sockeId, pReqHead, strlen(pReqHead), 0))
{
printf("发送数据失败");
closesocket(sockeId);
WSACleanup();
delete pReqHead;
return ;
}

delete pReqHead;

FILE *fp,*fRes;
fp = fopen("f:/down.apk", "wb+");
fRes = fopen("f:/down.txt","wb+");

if (NULL == fp)
{
printf("创建文件失败");
closesocket(sockeId);
WSACleanup();
return ;
}

char* buff = (char*)malloc(BuffSize*sizeof(char));
memset(buff,'\0',BuffSize);
int iRec = 0;
bool bStart = false;
while ((iRec =recv(sockeId, buff, BuffSize, 0)) > 0)
{
if(bStart){
fwrite(buff,iRec,1,fp);
}
else
{
char* pEnd = strstr(buff,"\r\n\r\n");
if(pEnd==NULL)
{
fwrite(buff,iRec,1,fp);
fwrite(buff,iRec,1,fRes);
}
else
{
bStart = true;
int iHead = pEnd+4-buff;
fwrite(pEnd+4,iRec-(iHead),1,fp);
fwrite(buff,iHead,1,fRes);
}
}
}

fclose(fp);
fclose(fRes);
free(buff);
closesocket(sockeId);
WSACleanup();
printf("下载结束");
}

int main(){
downLoad();
return 1;
}


收到服务器返回的数据后,我们就去找\r\n\r\n,这是部首结束的地方,也就是资源文件开始的地方,

由于我们发起请求时,connection的值是close,所以,服务器发送完资源文件后就会断开连接,因此,

我们不需要检查文件的长度,当iRec 等于0时,就表明数据接收结束了。在下载文件的同时,

我也把服务器的响应头信息记录了下来。

HTTP/1.1 200 OK
Server: ATS/4.0.2
Content-Type: application/vnd.android.package-archive
Content-Length: 5617051
Last-Modified: Sun, 16 Aug 2015 08:19:41 GMT
X-Down-Hit: lg-miui-file-mfs09.bj
Expires: Mon, 14 Sep 2015 03:58:02 GMT
Cache-Control: max-age=2419200
Date: Mon, 24 Aug 2015 01:19:49 GMT
Connection: close
Via: http/1.1 miCDN (ApacheTrafficServer/4.0.2 [cHs f ])


应该说,上面的程序非常简单,我们没有考虑connection : keep-alive的情况,也没有考虑分块编码的情况,

如果是长连接,或者是分块编码,那么我们的程序是需要作出相应的调整的。

下一篇博文将介绍http响应头和响应部首。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: