代理服务我们比较熟悉了,常见的代理方式有HTTP,sock4,sock5。到底浏览器是怎么通过代理服务器访问目标资源的呢,我这里
使用最简单的http代理服务来写个程序测试
一下。
首先用socket编程模拟正常的HTTP请求,winsock的使用方法请参看我以前的一篇博文——
链接
。
#include<WinSock2.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup();
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
/*
* 初始化套接字
*/
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(80);
connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));
/*
* 通信
*/
char req[1000] = "GET / HTTP/1.1\r\n\
Host: 127.0.0.1\r\n\
User-Agent: Mozilla/5.0\r\n\
Accept: */*\r\n\
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n\
Connection: keep-alive\r\n\r\n";//Proxy-Connection: Keep-Alive\r\nConnection: Keep-Alive\r\n
send(sockClient,req,strlen(req)+1,0);
char recvBuffer[1000];
memset(recvBuffer,NULL,sizeof(recvBuffer));
recv(sockClient,recvBuffer,1000,0);
printf("%s\n",recvBuffer);
closesocket(sockClient);
WSACleanup();
}
上面主要是模拟浏览器得HTTP请求头,发送GET命令以及请求头信息(
切记请求头部分不要缩进,请求头结尾处需要保留一个空行
),然后运行控制台程序得到如下结果,一个简单的http请求就实现了。
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Fri, 29 Jun 2012 02:55:54 GMT
Accept-Ranges: bytes
ETag: "a7c2ab3a255cd1:0"
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Fri, 29 Jun 2012 03:12:10 GMT
Content-Length: 5
Hello
请按任意键继续. . .
可以看到我们已经获取到网页资源了(响应状态码,响应头,响应正文),下面看看怎么通过HTTP代理来访问网页呢,其实原理都是一样的,
这时socket连接代理服务器而不是目标机器
(我这里用的fookproxy,一个简单易用的代理服务器,监听8000端口),示例代码如下,我们可以看出仅仅是发送的内容做了变化。
#include<WinSock2.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup();
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
/*
* 初始化套接字
*/
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(8000);
connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));
/*
* 通信
*/
char req[1000] = "GET http://www.baidu.com HTTP/1.1\r\n\
Host: www.baidu.com\r\n\
User-Agent: Mozilla/5.0\r\n\
Accept: */*\r\n\
Proxy-Connection: Keep-Alive\r\n\
Connection: keep-alive\r\n\r\n";
send(sockClient,req,strlen(req)+1,0);
char recvBuffer[1000];
memset(recvBuffer,NULL,sizeof(recvBuffer));
recv(sockClient,recvBuffer,1000,0);
printf("%s\n",recvBuffer);
closesocket(sockClient);
WSACleanup();
}
通过代理服务器访问百度网页,得到的响应内容如下(其中正文部分因为是二进制方式,没法打印出来)
HTTP/1.0 200 OK
Content-Length: 8024
Proxy-Connection: keep-alive
Set-Cookie: BAIDUID=FF55356604AA5C6335A9B1BB09A66659:FG=1; expires=Fri, 29-Jun-4
2 03:27:32 GMT; path=/; domain=.baidu.com
Expires: Fri, 29 Jun 2012 03:27:32 GMT
Server: BWS/1.0
Cache-Control: private
Date: Fri, 29 Jun 2012 03:27:32 GMT
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Content-Type: text/html;charset=gbk
请按任意键继续. . .
上面请求资源时必须写明
GET http://www.baidu.com
,而不能是
GET www.baidu.com
,不然代理服务器不知道目标资源是的什么协议,会返回如下错误信息
HTTP/1.0 502 [‘Server: Unsupported Scheme’, ‘Server: Unsupported Scheme’]
使用
HTTP
代理的方式非常简单,如果需要添加身份验证的话,需要在请求头中添加
Proxy-Authorization: Basic dHQ6MTIz
(其中最后那个字符串为用户名密码组合的
base64
编码),当然这之前代理服务器会返回客户端拒绝服务的提醒,并指定
WWW-Authenticate
响应头中包含身份验证方式(
basic
方式或者较为复杂安全的
NTLM
方式)。