C/C++ socket通过http代理服务器发送http请求

一、 前言

   网上使用C/C++ socket发送http请求的资料有很多很多。
   代理服务器的原理资料也不少。
   但是如何使用C/C++通过代理进行http请求的资(源)料(码)却没有找到。
   也不知道是不是我的关键字搜索的不对。。。
   所以准备自己研究一把。

 

二、 第一步:分析

   分析代理服务器原理:
   客户端 <==> 代理服务器  <==> 目的服务器
   看到这,灵感一闪,创建socket的时候应该使用“代理服务器”的地址和端口,而不是目的服务器的地址和端口,马上撸码测试。

 

三、 第二步:撸码测试

   使用“代理服务器”的地址和端口创建socket,然后把http request发送到“代理服务器”。
   编译运行。
   失败了,看来没有这么简单,得抓个包看看浏览器是怎么办到的。

 

四、 第三步:抓包

   我PC地址:192.168.32.165
   代理服务器地址:192.168.38.7
   目的地址:www.google.com
   
   使用chrome(浏览器已经配置了http代理)访问www.google.com,然后抓包,看看浏览器是如何实现代理上网的。
   发现了两条可疑数据,如下图:

"CONNECT www.google.com:443 HTTP/1.1"  443是默认的https端口

 

五、 第四步:再撸码测试

   编码发送"CONNECT www.google.com:443 HTTP/1.1"到代理服务器测试,
   编译运行,竟然成功了,不可思议。
   
   下面贴上测试代码,如有遗漏或者错误请大家帮忙指出,感激不尽!

#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib, "Ws2_32.lib")


#define     USE_PROXY       1
#define     PROXY_ADDR      "192.168.38.7"
#define     PROXY_PORT      3128


#if USE_PROXY
SOCKET _connect(const char *addr, unsigned short port)
#else
SOCKET _connect(struct in_addr *addr, unsigned short port)
#endif
{
    SOCKET sockfd = INVALID_SOCKET;
    struct sockaddr_in sin = { 0 };
    int ret = 0;

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == sockfd)
    {
        printf("socket failed.\n");
        return sockfd;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
#if USE_PROXY
    sin.sin_addr.s_addr = inet_addr(addr);
#else
    sin.sin_addr = *addr;
#endif
    ret = connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in));
    if (0 != ret)
    {
        printf("socket failed.\n");
        closesocket(sockfd);
        sockfd = INVALID_SOCKET;
    }

    return sockfd;
}

void _close(SOCKET sockfd)
{
    if (INVALID_SOCKET != sockfd)
    {
        closesocket(sockfd);
    }
}

void connect_proxy(SOCKET sockfd)
{
    // proxy user and password: dXNlcm5hbWU6cGFzc3dvcmQ= is "username:password" with base64 encode
    const char *header = "CONNECT www.baidu.com:80 HTTP/1.1\r\nProxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=\r\n\r\n";
    int offset = 0;
    char buf[1024] = { 0 }; // buf maybe not enough, to malloc it dynamically...

    send(sockfd, header, strlen(header), 0);

    // if success proxy server will response "HTTP/1.0 200 Connection established\r\n\r\n"
    // TODO... check response
    while (1)
    {
        recv(sockfd, buf+offset, 1, 0);
        offset++;
        if (offset >= 4)
        {
            if ('\n' == buf[offset - 1]
                && '\r' == buf[offset - 2]
                && '\n' == buf[offset - 3]
                && '\r' == buf[offset - 4])
            {
                break;
            }
        }
    }
    printf("%s", buf);
}

void http_request()
{
    SOCKET sockfd = INVALID_SOCKET;
    struct hostent *host = NULL;
    char c;
    const char *header = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n";;

#if USE_PROXY
    sockfd = _connect(PROXY_ADDR, PROXY_PORT);
    connect_proxy(sockfd);
#else
    host = gethostbyname("www.baidu.com");
    sockfd = _connect((struct in_addr*)host->h_addr_list[0], 80);
#endif

    send(sockfd, header, strlen(header), 0);

    // Just printf response
    while (1)
    {
        recv(sockfd, &c, 1, 0);
        printf("%c", c);

        // if recv all
        // break;
    }

    _close(sockfd);
}

int main()
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    http_request();

    WSACleanup();
    return 0;
}

下面是运行结果

 


版权声明:本文为yu1121jm原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。