ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

使用WinSock制作一个简单的代理服务器(C++实现)

2020-12-31 16:31:19  阅读:297  来源: 互联网

标签:cout int recv sock C++ local 代理服务器 buf WinSock


注意

  1. 只能访问HTTP网站
  2. 只能访问简单页面,纯HTML
  3. 性能很差,代码逻辑和结构还有很多不完整的地方
  4. 参考:c-winsock-proxy-problem

代码

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <map>
//only used in vc++
//#pragma comment( lib, "ws2_32.lib" )


using namespace std;
map<int,string> errorMap;
//代理服务LISTEN端口8080
int port = 5522;

SOCKET listen_sock;
SOCKET client_sock;

char FR_recv_buf [1024000] = "";
//char * FR_recv_buf = (char *) malloc(sizeof(999999999));
//char * FR_recv_buf = NULL;


char recv_buf [1024000] = "";

int Receive();
int Listen();

//初始化winsock
bool InitializeWinsock()
{

    errorMap[10004] = "WSAEINTR Interrupted function call.";
    errorMap[10038] = "WSAENOTSOCK Socket operation on nonsocket.";
    errorMap[10093] = "WSANOTINITIALISED Successful WSAStartup not yet performed";
    // 指针指向WSADATA数据结构,接受winsock实现的细节
    WSADATA wsaData;

    //WSAStartup必须是程序中第一个调用的winsocke函数,只有当其成功调用后才能使用进一步的winsocket函数
    int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(iResult != 0)
    {
        //不成功将返回错误码,下面是几种可能的错误码
        //10022 WSAEINVAL 应用程序指出的Windows Sockets 版本不被该DLL 支持.
        //10092 WSAVERNOTSUPPORTED 所需的Windows Sockets API 的版本未由特定的Windows Sockets 实现提供.
        //10091 WSASYSNOTREADY 由WSAStartup()函数返回,表明底层的网络子系统无法使用。
        cout << "WinSock启动失败,错误码: " << iResult << endl;
        return false;
    }
    else
    {
        cout << "WinSock成功初始化。代理服务器成功启动" << endl;
        return true;
    }
}

//Forward
int ForwardResponse()
{
    //客户端socket出错,抛出异常
    if (send(client_sock, FR_recv_buf, sizeof(FR_recv_buf), 0) == SOCKET_ERROR)
    {
        cout << "向客户端send()出错,错误码: " << WSAGetLastError() << endl;
        closesocket(client_sock);
        return 0;
    }
    else
    {

//        cout << "向客户端发送如下数据:" << endl;
//        cout << FR_recv_buf << endl;
        cout << "向客户端send()成功.\n";
        memset(&FR_recv_buf,0,sizeof(FR_recv_buf));
        //go back to begginning again?
        Receive();
//        CreateThread(0,0,(LPTHREAD_START_ROUTINE)Receive, 0, 0,0);
    }
    return 1;
}

//Function to parse hostname from http request
//从rev_buf中寻找目的地的hostname
string ParseHostname(char * buf)
{
    size_t pos;

    //copy request to string for easier parsing
    string httpheader = buf;

    //string to hold hostname substring
    string hostname_t;


    //找到buffer中的Host所在行之后,pos + 6得到完整的hostname
    pos = httpheader.find("Host: ");
    hostname_t = httpheader.substr(pos + 6);

    //找到行尾的回车换行符\r\n
    pos = hostname_t.find("\r\n");

    //擦除行尾其他部分
    hostname_t.erase(pos);

    return hostname_t;
}

//Function to forward HTTP request from browser to webserver
// 将存在recv_buf中的数据转发给远程服务器
int ForwardRequest()
{
    int bytes_received;
    SOCKADDR_IN Dest;
    SOCKET frecv_sock;
    hostent *Host;

    cout << "开始处理客户端的转发请求" << endl;
    //获取buffer请求中的hostname
    string hostname = ParseHostname(recv_buf);

//  gethostbyname用于通过给定的hostname得到hostent结构,结构如下:
//    struct hostent
//    {
//        char FAR * h_name; /* official name of host */
//        char FAR * FAR * h_aliases; /* alias list */
//        short h_addrtype; /* host address type */
//        short h_length; /* length of address */
//        char FAR * FAR * h_addr_list; /* list of addresses */
//        #define h_addr h_addr_list[0] /* address, for backward compat,此处用于向后兼容*/
//    };
//  发生错误将返回空指针

    if((Host=gethostbyname(hostname.c_str()))==NULL)
    {
        //如果返回空指针,查找winsock报错原因进行如下处理
        DWORD dwError = WSAGetLastError();
        if (dwError != 0)
        {
            if(dwError == WSAHOST_NOT_FOUND)
            {
                cout << "客户端请求的Host主机: " << hostname.c_str()  << " 无法找到\n";
                WSACleanup();
                return FALSE;
            }
            else if (dwError == WSANO_DATA)
            {
                cout << "有效的Host,但无法直接访问,通常由于DNS解析有误\n";;
                WSACleanup();
                return FALSE;
            }
            else
            {
                cout << "查找远程服务器时发生严重错误,错误码: " << dwError << endl;
                WSACleanup();
                return FALSE;
            }
        }
    }
    else
    {
        //如果返回的不是空指针,说明host指针获取成功
        cout << "成功获取到客户端请求中的HostName: " <<  hostname.c_str() << endl;
    }

    Dest.sin_family=AF_INET;
    cout<< "即将与远程服务器的80端口进行连接"<<endl;
    Dest.sin_port=htons(80);


    //如下命令从将从复制Host的h_addr到Dest的sin_addr,长度为address的长度
    memcpy(&Dest.sin_addr,Host->h_addr,Host->h_length);

    // Create a SOCKET for connecting to server
    //新建socket连接远程服务器
    if((frecv_sock = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
    {
        cout << "建立与远程服务器的socket失败,错误码:" << WSAGetLastError() << endl;

        closesocket(frecv_sock);
        return FALSE;
    }

    // Connect to server
    //连接服务器
    // int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR* name,int namelen);
    // 这里使用SOCKET_STREAM,使用名字与远程连接建立连接,成功调用则可以开始进行发送数据
    if(connect( frecv_sock,(SOCKADDR*)&Dest,sizeof(Dest))==SOCKET_ERROR)
    {
        cout << "与远程服务器connect()失败,错误码: " << WSAGetLastError() << endl;
        closesocket( frecv_sock);
        return FALSE;
    }

    //send intercepted request to server
    //已经与服务器建立了连接,可以开始发送数据
    //int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);
    if (send(frecv_sock, recv_buf, strlen(recv_buf), 0) == SOCKET_ERROR)
    {
        cout << "向远程服务器send()失败,错误码: " << WSAGetLastError() << endl;
        closesocket(frecv_sock);
        return 0;
    }
    else
    {
        //成功向远程服务器发送数据,将会返回发送数据的总数
        cout << "向远程服务器send()成功,发送了"<< strlen(recv_buf)<< "Bytes"<<endl;
        memset(&recv_buf,0,sizeof(recv_buf));
    }

    //receive request from server
    //从远程服务器接收数据
    int i = 1;
    do
    {
        //接收数据,在FR_recv_buf中接受从远程服务器传送的数据,flags为标志位参数
        //int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags);
        bytes_received = recv(frecv_sock,FR_recv_buf,sizeof(FR_recv_buf) - 1,0);
        cout << bytes_received << endl;
        if (bytes_received > 0)
        {
            //没有错误发生,正常收到了数据,现在将开启一个子线程用来转发FR_recv_buf

            cout << "第"<< i <<"次: "<<"从远程服务器recv()成功,收到 " << bytes_received << " Bytes" << endl;
//            cout << "第" << i << "次从远程服务器recv()到的内容为:" << endl;
//            cout << FR_recv_buf << endl;
            cout << "第" << i << "次 建立线程将从远程客户端收到的响应写回客户端" << endl;
            strcat (FR_recv_buf, "\0");
            CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ForwardResponse, 0,0,0);

            i ++;


            //ForwardResponse();
        }
        else if ( bytes_received == 0 )
        {
            //连接已终止,返回0

            cout << "从远程服务器recv()完成,结束与远程服务器的连接\n";
            closesocket(frecv_sock);
        }
        else if ( bytes_received == SOCKET_ERROR)
        {
            cout << "从远程服务器recv()失败,错误码: " << WSAGetLastError() << endl;
            closesocket(frecv_sock);
            //WSACleanup();
            return 0;
        }
    }
    while (bytes_received > 0);
    return 1;
}

//Function to accept connection and receive data from browser
int Receive()
{
    SOCKADDR_IN csin;
    int csin_len = sizeof(csin);
    int iResult;

    //accept client connection
//  在一个套接口接受一个连接
//  listen_sock套接口描述字
//  csin为指向缓冲区的指针
//  指向csin长度的指针

    client_sock = accept(listen_sock, (LPSOCKADDR)&csin, &csin_len);
    //pauses here to wait for connection from client
    if (client_sock == INVALID_SOCKET)
    {
        cout << "accept()客户端连接出错: "<< WSAGetLastError() << endl;
        closesocket(client_sock);
        //WSACleanup();
        exit(0);
        return 1;
    }
    else
    {
        cout << "客户端已连接: " << inet_ntoa(csin.sin_addr) << ":" << csin.sin_port << endl;
    }
    //Start another thread to accept.
//  递归开启另一个客户端线程

    cout << "开启新的Receive()线程供客户端连接" << endl;

    CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Receive, 0, 0,0);

    do
    {
//      从一个套接口接受数据
//      client_sock表示已连接的套接口描述字
//      recv_buf用于接受数据的缓冲区
//      sizeof(recv_buf)缓冲区长度
//      0指定调用方式,读取tcp 缓冲区中的数据到buffer中,并从tcp 缓冲区中移除已读取的数据
//      无错误返回读入的字节数
        iResult = recv(client_sock, recv_buf, sizeof(recv_buf) - 1, 0);
        strcat(recv_buf, "\0");
        if (iResult == SOCKET_ERROR)
        {
            closesocket(client_sock);
            cout << "从客户端recv()出错,错误码: "<< WSAGetLastError() << endl;
        }
        else if (iResult > 0)
        {
//          将\0追加在recv_buffer后

            cout <<"从客户端recv()成功,共收到: " << iResult << "Bytes" <<endl;
            //forward HTTP request from browser to web server
            cout << recv_buf << endl;
            //此时recv_buf中已经存储了从客户端收到的请求信息,
            //现在需要将请求转发给目的地并接受响应,并写回给客户端
            //开启子线程处理这个过程
            HANDLE pChildThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ForwardRequest, 0, 0,0);
            WaitForSingleObject(pChildThread,6000);  //Wait for connection between proxy and remote server
            CloseHandle(pChildThread);
        }
        else if ( iResult == 0 )
        {
            cout << "从客户端recv()结束,断开与客户端的连接\n";
        }
    }
    while ( iResult > 0 );
    return 0;
}

//Function which listens for incoming connections to the proxy
int Listen()
{
    SOCKADDR_IN local;
//    socket地址
//    struct sockaddr_in
//    {
//        short sin_family;
//        u_short sin_port;
//        struct in_addr sin_addr;
//        char sin_zero[8];
//    };

//  内存空间初始化,将local指向的内存字节全部置为0,避免出现莫名奇妙的值,memset是一种对结构或数组清零的最快方法
    memset(&local,0,sizeof(local));

//  AF_INET指定TCP/IP协议簇
    local.sin_family = AF_INET;

//  htons将主机的16位无符号短整形数转换成网络字节顺序
    local.sin_port = htons(port);


//  如果一个应用并不关心分配给它的地址,则可将Internet 地址设置为INADDR_ANY,或将端口号置为0。
//  如果Internet 地址段为INADDR_ANY,则可使用任意网络接口;在有多种主机环境下可简化编程。
//  如果端口号置为0,则WINDOWS 套接口实现将给应用程序分配一个值在1024 到5000 之间的唯一的端口。
    local.sin_addr.s_addr = INADDR_ANY;


//  create socket for listening to
//  指定地址格式AF_INET,
//  数据类型为流式套接口(可靠的,双向的基于连接的字节流,使用带外数据传送机制),
//  0为协议缺省
//  成功将返回一个引用新套接口的描述字
    listen_sock = socket(AF_INET, SOCK_STREAM, 0);

    //bind function associates a local address with a socket.
//  将一个socket与本地地址绑定
//  成功将返回0,错误将返回SOCKET_ERROR,可通过错误码来进行追踪
    if (bind(listen_sock, (LPSOCKADDR)&local, sizeof(local)) == 0)
    {
        cout << "服务端bind()成功" << endl;
//      创建一个套接口,并监听申请的连接
//      5表示等待连接队列的最大长度,默认为5,队列已满将返回WSAECONNREFUSED(10061)
        if (listen(listen_sock, 10) == 0)
        {
            cout << "服务端listen()成功" << endl;
            cout << "WinSocket正在监听端口: " << port << endl;
        }
        else
        {
            cout << "无法监听端口.\n";
        }
    }
    else
    {
        cout << "服务端bind()出错,错误码: "<< WSAGetLastError() << endl;
    }

    //accept and start receiving data from browser
//    HANDLE CreateThread(
//        LPSECURITY_ATTRIBUTES   lpThreadAttributes, 内核安全属性
//        SIZE_T                  dwStackSize, 线程栈空间大小
//        LPTHREAD_START_ROUTINE  lpStartAddress, 新线程执行线程的函数地址
//        __drv_aliasesMem LPVOID lpParameter, 传递给线程函数的参数
//        DWORD                   dwCreationFlags, 控制线程运行的标识符,0表示在创建后立即运行
//        LPDWORD                 lpThreadId 返回线程的ID号
//    );

//  创建线程接收来自客户端的连接
    CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Receive, 0, 0,0);
    return 0;
}

int CloseServer()
{
    closesocket(client_sock);
    WSACleanup();
    return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    InitializeWinsock();
    Listen();
    cin.get();
    return 0;
}

标签:cout,int,recv,sock,C++,local,代理服务器,buf,WinSock
来源: https://blog.csdn.net/lijiext/article/details/112023433

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有