欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++中HTTP?代理服務(wù)器的設(shè)計(jì)與實(shí)現(xiàn)詳解

 更新時(shí)間:2024年01月08日 14:02:06   作者:江水為竭  
代理服務(wù)器,即允許一個(gè)網(wǎng)絡(luò)終端(一般為客戶(hù)端)通過(guò)這個(gè)服務(wù)與另一?個(gè)網(wǎng)絡(luò)終端(一般為服務(wù)器)進(jìn)行非直接的連接,下面我們就來(lái)看看如何使用C++設(shè)計(jì)與實(shí)現(xiàn)一個(gè)HTTP?代理服務(wù)器吧

實(shí)驗(yàn)內(nèi)容

設(shè)計(jì)并實(shí)現(xiàn)一個(gè)基本 HTTP 代理服務(wù)器。要求在指定端口(例如 8080)接收來(lái)自客戶(hù)的 HTTP 請(qǐng)求并且根據(jù)其中的 URL 地址訪(fǎng)問(wèn)該地址 所指向的 HTTP 服務(wù)器(原服務(wù)器),接收 HTTP 服 務(wù)器的響應(yīng)報(bào)文,并 將響應(yīng)報(bào)文轉(zhuǎn)發(fā)給對(duì)應(yīng)的客戶(hù)進(jìn)行瀏覽。

設(shè)計(jì)并實(shí)現(xiàn)一個(gè)支持 Cache 功能的 HTTP 代理服務(wù)器。要求能緩 存原服務(wù)器響應(yīng)的對(duì)象,并 能夠通過(guò)修改請(qǐng)求報(bào)文(添加 if-modified-since 頭行),向原服務(wù)器確認(rèn)緩存對(duì)象是否是最新版本。 (選作內(nèi)容)

擴(kuò)展 HTTP 代理服務(wù)器,支持如下功能: (選作內(nèi)容)

網(wǎng)站過(guò)濾:允許/不允許訪(fǎng)問(wèn)某些網(wǎng)站;

用戶(hù)過(guò)濾:支持/不支持某些用戶(hù)訪(fǎng)問(wèn)外部網(wǎng)站;

網(wǎng)站引導(dǎo):將用戶(hù)對(duì)某個(gè)網(wǎng)站的訪(fǎng)問(wèn)引導(dǎo)至一個(gè)模擬網(wǎng)站(釣魚(yú))。

代理服務(wù)器的概念

代理服務(wù)器,允許一個(gè)網(wǎng)絡(luò)終端(一般為客戶(hù)端)通過(guò)這個(gè)服務(wù)與另一 個(gè)網(wǎng)絡(luò)終端(一般為服務(wù)器)進(jìn)行非直接的連接。普通 Web 應(yīng)用通信方式與采用代理服務(wù)器的 通信方式的對(duì)比如下圖所示:

代理服務(wù)器在指定端口(本實(shí)驗(yàn)中所指定的是666端口)監(jiān)聽(tīng)瀏覽器的訪(fǎng)問(wèn)請(qǐng)求(需要在客戶(hù)端瀏覽器進(jìn)行相應(yīng)的設(shè)置),接收到瀏覽器對(duì)遠(yuǎn)程網(wǎng)站的瀏覽請(qǐng)求時(shí),代理服務(wù)器開(kāi)始在代理服務(wù)器的緩存中檢索 URL 對(duì)應(yīng)的對(duì)象(網(wǎng)頁(yè)、 圖像等對(duì)象),找到對(duì)象文件后,提取該對(duì)象文件的最新被修改時(shí)間;代理服務(wù)器程序在客戶(hù)的請(qǐng)求報(bào)文首部插入,并向原 Web 服務(wù)器轉(zhuǎn)發(fā)修改后的請(qǐng)求報(bào)文。如果代理服務(wù)器沒(méi)有該對(duì)象的緩存,則會(huì)直接向原服務(wù)器轉(zhuǎn)發(fā)請(qǐng)求報(bào)文,并將原服務(wù)器返回的響應(yīng)直接轉(zhuǎn)發(fā)給客戶(hù)端,同時(shí)將對(duì)象緩存到代理服務(wù)器中。代理服務(wù)器程序會(huì)根據(jù)緩存的時(shí)間、大小和提取記錄等對(duì)緩存進(jìn)行清理。

代碼結(jié)構(gòu)

代碼中共實(shí)現(xiàn) 3個(gè)類(lèi),分別為WebsiteDetector類(lèi)、Cache類(lèi)和HttpProxyServer類(lèi)。

WebsiteDetector類(lèi)

該類(lèi)實(shí)現(xiàn)了網(wǎng)站過(guò)濾和網(wǎng)站引導(dǎo)功能。通過(guò)構(gòu)造函數(shù)直接靜態(tài)設(shè)置了釣魚(yú)網(wǎng)站和屏蔽的網(wǎng)站:

highlighter- reasonml

WebsiteDetector::WebsiteDetector() 
{
	AddValidURL("http://jwc.hit.edu.cn/","http://jwts.hit.edu.cn/");
	AddBlockedURL("http://xltj.hit.edu.cn/");
}

可知,屏蔽了心理網(wǎng)站。將教務(wù)處網(wǎng)站引導(dǎo)到本科教學(xué)管理與服務(wù)平臺(tái)。

Cache類(lèi)

該類(lèi)在當(dāng)前目錄下創(chuàng)建文件夾.cache/,在其中存儲(chǔ)瀏覽緩存對(duì)象。同時(shí)該類(lèi)中,保存著對(duì)象與文件名的映射關(guān)系,對(duì)象和LastModified字段的映射關(guān)系。

highlighter- cpp

class Cache {

public:

std::string GetDate(const std::string& url); // 獲取url對(duì)應(yīng)保存的LastModified字段

bool Get(const std::string& url, char* response, size_t& start, size_t& responseSize); // 讀取緩存

bool Put(const std::string& url, const char* response, size_t responseSize, size_t& start); // 保存緩存

private:

?      std::string cacheDirectory_; // 存放緩存的文件目錄

?      std::map<std::string, std::string> cacheMap_; // 對(duì)象和LastModified字段的映射關(guān)系

?      std::map<std::string, std::string> fileMap_; / 對(duì)象與文件名的映射關(guān)系

?      std::mutex mutex_; // 多線(xiàn)程同時(shí)讀寫(xiě)文件的互斥鎖

};

HttpProxyServer類(lèi)

該類(lèi)是代理服務(wù)器的實(shí)現(xiàn)類(lèi)。是一個(gè)多用戶(hù)代理服務(wù)器。首先該類(lèi)創(chuàng)建HTTP代理服務(wù)的TCP主套接字,該套接字監(jiān)聽(tīng)等待客戶(hù)端的連接請(qǐng)求。當(dāng)客戶(hù)端連接之后,創(chuàng)建一個(gè)子線(xiàn)程,由子線(xiàn)程行上述一對(duì)一的代理過(guò)程,服務(wù)結(jié)束之后子線(xiàn)程終止。

highlighter- reasonml

class HttpProxyServer

{

public:

	HttpProxyServer(int port); // 構(gòu)造函數(shù),參數(shù)為端口號(hào)

	void Start(); // 監(jiān)聽(tīng)客戶(hù)端連接請(qǐng)求

private:

	int serverSocket_; // 代理服務(wù)Socket

	int port_; // 端口號(hào)

	struct sockaddr_in serverAddr_; // 代理服務(wù)地址

	Cache cache_; // Cache類(lèi)

	WebsiteDetector websiteDetector_; // websiteDetector類(lèi)

	void HandleClient(int clientSocket); // 子線(xiàn)程調(diào)用函數(shù)

	std::string ExtractUrl(const std::string &httpRequest); // 解析URL

	int CreateServerSocket(const std::string &host); // 創(chuàng)建與原服務(wù)器連接的Socket

	bool ServerToClient(const std::string &url, int clientSocket); // 轉(zhuǎn)發(fā)數(shù)據(jù)

	void ParseUrl(const std::string &url, std::string &host, std::string &path); // 解析主機(jī)名與路徑名

};

程序基本流程

(1) 初始化服務(wù)器Socket,監(jiān)聽(tīng)等待客戶(hù)端的連接請(qǐng)求。

(2) 當(dāng)客戶(hù)端連接后,創(chuàng)建子線(xiàn)程處理請(qǐng)求。

(3) 子線(xiàn)程接收請(qǐng)求,解析HTTP請(qǐng)求的首部行和請(qǐng)求頭。然后提取Url,Url作為參數(shù)通過(guò)websiteDetector類(lèi)判斷是否屏蔽或者引導(dǎo)。

(4) 然后進(jìn)入轉(zhuǎn)發(fā)的過(guò)程,首先進(jìn)行域名解析,然后創(chuàng)建Socket先原服務(wù)器發(fā)送請(qǐng)求,接收響應(yīng),將數(shù)據(jù)轉(zhuǎn)發(fā)到客戶(hù)端。

(5) 在轉(zhuǎn)發(fā)的過(guò)程中,涉及保存緩存和讀取緩存。

網(wǎng)站引導(dǎo)功能

利用首部行中的location字段,實(shí)現(xiàn)引導(dǎo)。

highlighter- arduino

std::string locationResponse = std::string("HTTP/1.1 302 Found") + MY_CRLF + "Location: " + newUrl + MY_CRLF + MY_CRLF;

send(clientSocket, locationResponse.c_str(), locationResponse.size(), 0);

用戶(hù)過(guò)濾功能

設(shè)置服務(wù)器地址信息時(shí)實(shí)現(xiàn)。

highlighter- awk

serverAddr_.sin_addr.s_addr = INADDR_ANY;

// serverAddr_.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //只允許本機(jī)用戶(hù)訪(fǎng)問(wèn)服務(wù)器

Cache功能

1. 代理服務(wù)器處理客戶(hù)端請(qǐng)求時(shí),對(duì)于第一次出現(xiàn)的對(duì)象,會(huì)保存下。當(dāng)客戶(hù)端再次請(qǐng)求時(shí),代理服務(wù)器就會(huì)在請(qǐng)求中添加If-Modified-Since首部行。

highlighter- qml

date = cache_.GetDate(url);
std::string cacheRequest = httpRequest + "If-Modified-Since: " + date + MY_CRLF + MY_CRLF;

發(fā)送該請(qǐng)求后,等待原服務(wù)器響應(yīng),并判斷是否回應(yīng)304狀態(tài)碼。

highlighter- reasonml

if (IsResponseNotModified(responseNotModified) ) {
?      // std::cout << "304 Not Modified" << std::endl;
?      sel = false;
}else {
?      cache_.ClearFileContent(url); //清空
?      sel = true;
}

sel為false時(shí),則讀取Cache轉(zhuǎn)發(fā)到客戶(hù)端。若為true,則發(fā)送HTTP請(qǐng)求到原服務(wù)器,再接收響應(yīng),轉(zhuǎn)發(fā)到客戶(hù)端,再保存到Cache。

修改Chrome瀏覽器代理配置

--proxy-server="http://127.0.0.1:666"

VScode編譯運(yùn)行

該代理服務(wù)器成功在666端口啟動(dòng),并輸出了cache目錄。

驗(yàn)證

驗(yàn)證基礎(chǔ)的代理功能

訪(fǎng)問(wèn)今日哈工大網(wǎng)站:http://today.hit.edu.cn

可以看到,網(wǎng)站資源順利加載,輸出欄中,輸出了請(qǐng)求的各個(gè)資源對(duì)象的url。

驗(yàn)證網(wǎng)站引導(dǎo)功能

輸入網(wǎng)址:http://jwc.hit.edu.cn/

最后直接跳轉(zhuǎn)到到了,http://jwts.hit.edu.cn/

驗(yàn)證網(wǎng)站過(guò)濾功能

輸入網(wǎng)址:http://xltj.hit.edu.cn/

可以看到,無(wú)法訪(fǎng)問(wèn)。

驗(yàn)證用戶(hù)過(guò)濾功能

驗(yàn)證Cache功能

將在Cache中的資源 http://jwts.hit.edu.cn/resources/css/common/ydy.css ,修改一下。

把色彩均改為紅色,再次訪(fǎng)問(wèn) http://jwts.hit.edu.cn/

可以看到,字體顏色變?yōu)榧t色??芍琀TTP代理服務(wù)器這次使用的是Cache中的資源。

源代碼

//g++ your_code.cpp -o your_executable -lws2_32

#include <fstream>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <thread>
#include <vector>
#include <mutex>
#include <list>
#include <map>
#include <sstream>
#include <winsock2.h>
#include <WS2tcpip.h>



#define MAX_CLIENTS 6
#define BUFSIZE 655360
#define HEADSIZE 128
#define MY_CRLF "\r\n"

class WebsiteDetector {
public:
    WebsiteDetector() 
    {
        AddValidURL("http://jwc.hit.edu.cn/", "http://jwts.hit.edu.cn/");
        AddBlockedURL("http://xltj.hit.edu.cn/");
    }
    // 釣魚(yú)
    std::string IsURLPhishing(const std::string& url) {
        auto it = validURLs_.find(url);
        if (it != validURLs_.end()) {
            return it->second;
        } else {
            return "Phishing";
        }
    }
    // 屏蔽
    bool IsURLBlocked(const std::string& url) {
        for (const std::string& blockedURL : blockedURLs_) {
             if (url.find(blockedURL) != std::string::npos) {
                return true;
            }
        }
        return false;
    }
private:
    std::map<std::string, std::string> validURLs_;
    std::vector<std::string> blockedURLs_;

    void AddValidURL(const std::string& srcURL, const std::string& dstURL) 
    {
        validURLs_[srcURL] = dstURL;
    }

    void AddBlockedURL(const std::string& url) {
        blockedURLs_.push_back(url);
    }
};

class Cache {
public:
   
    Cache() : cacheDirectory_("H:\\cppwork\\CS-networking\\.cache") {
        std::cout << cacheDirectory_ << std::endl;
        std::system(("mkdir -p " + cacheDirectory_).c_str());
    }
    bool Check(const std::string& url) {
        std::lock_guard<std::mutex> lock(mutex_);
        auto it = cacheMap_.find(url);
        if (it != cacheMap_.end()) 
        {
            return true;
        }
        return false;
    }
    // 清空文件內(nèi)容
    bool ClearFileContent(const std::string& url) {
        std::lock_guard<std::mutex> lock(mutex_);

        // Generate a unique filename based on the URL
        std::string fileName = GetFileNameFromUrl(url);
        auto it = fileMap_.find(fileName);
        std::string fileTag = it->second;

        std::string filePath = cacheDirectory_ + "\\" + fileTag;
        // 打開(kāi)文件并使用 std::ios::trunc 模式來(lái)清空文件內(nèi)容
        std::ofstream file(filePath, std::ios::out | std::ios::trunc);
        if (!file) {
            std::cerr << "無(wú)法打開(kāi)文件:" << filePath << std::endl;
            return false;
        }
        // 關(guān)閉文件
        file.close();
        return true;
    }
    std::string GetDate(const std::string& url) 
    {
        std::lock_guard<std::mutex> lock(mutex_);
        auto it = cacheMap_.find(url);
        return it->second;
    }
    bool Get(const std::string& url, char* response, size_t& start, size_t& responseSize) {
        std::lock_guard<std::mutex> lock(mutex_);

        // Generate a unique filename based on the URL
        std::string fileName = GetFileNameFromUrl(url);
        std::string fileTag = fileMap_[fileName];

        std::cout << "Get() url: " << url << std::endl;
        std::cout << "Get() fileTag: " << fileTag << std::endl;
        // If found, read the response from the file
        std::ifstream file(cacheDirectory_ + "\\" + fileTag, std::ios::binary);
        if (file) {
            file.seekg(start, std::ios::beg);
            file.read(response, BUFSIZE);
            // Get the number of bytes read in this chunk
            size_t bytesRead = static_cast<size_t>(file.gcount());
            start += bytesRead;
            responseSize = bytesRead;
            response[bytesRead] = '\0';
            file.close();
            return true;
        }
        return false; // URL not found in the cache
    }

    bool Put(const std::string& url, const char* response, size_t responseSize, size_t& start) {
        std::lock_guard<std::mutex> lock(mutex_);

        // Generate a unique filename based on the URL
        std::string fileName = GetFileNameFromUrl(url);
        auto it = fileMap_.find(fileName);
        std::string fileTag;
        if (it == fileMap_.end()) 
        {
            fileTag = std::to_string(cnt);
            fileMap_[fileName] = fileTag;
            cnt ++;
        }else 
        {
            fileTag = it->second;
        }

        // Store the response in a file
        std::ofstream file(cacheDirectory_ + "\\" + fileTag, std::ios::binary | std::ios::app );
        if (!file) {
            fprintf(stderr, "file open error: %s(errno: %d)\n", strerror(errno),errno);
            return false; // Unable to open file for writing
        }
        file.seekp(start);
        file.write(response, responseSize);
        file.close();
        start += responseSize;
        return true; // Failed to store response in the cache
    }

    void PutDate(const std::string& url)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        // Generate a unique filename based on the URL
        std::string fileName = GetFileNameFromUrl(url);
        std::string fileTag = fileMap_[fileName];
        // 拼接完整的文件路徑
        std::string filePath = cacheDirectory_ + "\\" + fileTag;

        // 打開(kāi)文件并讀取 Last-Modified 首部?jī)?nèi)容
        std::ifstream file(filePath);
        if (!file) {
            fprintf(stderr, "file open error: %s(errno: %d)\n", strerror(errno),errno);
        }
        std::string line;
        while (std::getline(file, line)) {
            // 查找包含 Last-Modified 首部的行
            if (line.find("Last-Modified:") != std::string::npos) {
                // 提取 Last-Modified 的值并存儲(chǔ)到 cacheMap_
                size_t startPos = line.find(":") + 2;
                size_t endPos = line.find(MY_CRLF);
                std::string date = line.substr(startPos, endPos);
                // std::cout << "line: " << line << std::endl;
                // std::cout << "date: " << date << std::endl;
                cacheMap_[url] = date;
                break; // 找到后可以退出循環(huán)
            }else
            {
                if (line == MY_CRLF)
                {   
                    break;
                }
            }
        }
        file.close();
    }

    
private:
    std::string cacheDirectory_;
    std::map<std::string, std::string> cacheMap_;
    std::map<std::string, std::string> fileMap_;
    std::mutex mutex_;
    int cnt = 1;

    std::string GetFileNameFromUrl(const std::string& url) {
        // Replace characters in the URL to create a valid filename
        std::string fileName = url;
        for (char& c : fileName) {
            if (c == '/' || c == '?' || c == '&' || c == '=') {
                c = '_';
            }
        }
        return fileName;
    }
};


// 定義HTTP代理服務(wù)器類(lèi)
class HttpProxyServer
{
public:
    HttpProxyServer(int port) : port_(port)
    {
        
        // 初始化服務(wù)器
        // 創(chuàng)建主套接字并綁定端口
        serverSocket_ = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket_ == -1)
        {
            fprintf(stderr, "Constructor(): create socket error: %s(errno: %d)\n", strerror(errno),errno);
            exit(EXIT_FAILURE);
        }

        // 設(shè)置服務(wù)器地址信息
        // 初始化 serverAddr_
        memset(&serverAddr_, 0, sizeof(serverAddr_));
        serverAddr_.sin_family = AF_INET;
        serverAddr_.sin_addr.s_addr = INADDR_ANY;
        // serverAddr_.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //只允許本機(jī)用戶(hù)訪(fǎng)問(wèn)服務(wù)器
        serverAddr_.sin_port = htons(port_);

        // 綁定套接字到指定端口
        if (bind(serverSocket_, (struct sockaddr *)&serverAddr_, sizeof(serverAddr_)) == -1)
        {
            fprintf(stderr, "Constructor(): bind socket error: %s(errno: %d)\n",strerror(errno), errno);
            closesocket(serverSocket_);
            exit(EXIT_FAILURE);
        }

        // 開(kāi)始監(jiān)聽(tīng)客戶(hù)端連接請(qǐng)求
        if (listen(serverSocket_, MAX_CLIENTS) == -1)
        {
            fprintf(stderr, "Constructor(): listen socket error: %s(errno: %d)\n",strerror(errno),errno);
            closesocket(serverSocket_);
            exit(EXIT_FAILURE);
        }

        std::cout << "Proxy server started on port " << port_ << std::endl;
    }

    void Start()
    {
        // 啟動(dòng)服務(wù)器,監(jiān)聽(tīng)客戶(hù)端連接請(qǐng)求
        while (true)
        {
            struct sockaddr_in clientAddr;
            int clientAddrLen = sizeof(struct sockaddr);

            // 接受客戶(hù)端連接
            int clientSocket = accept(serverSocket_, (struct sockaddr *)&clientAddr, &clientAddrLen);
            if (clientSocket == INVALID_SOCKET)
            {
                fprintf(stderr, "Start(): accept socket error: %s(errno: %d)",strerror(errno),errno);
                continue; // 繼續(xù)等待下一個(gè)連接
            }

            // std::cout << "Start(): Accepted a client connection" << std::endl;

            // 創(chuàng)建子線(xiàn)程處理客戶(hù)端請(qǐng)求
            std::thread clientThread(&HttpProxyServer::HandleClient, this, clientSocket);
            clientThread.detach(); // 不等待
        }
    }

private:
    int serverSocket_;
    int port_;
    struct sockaddr_in serverAddr_;
    Cache cache_;
    WebsiteDetector websiteDetector_;

    void HandleClient(int clientSocket)
    {
        // 讀取客戶(hù)端的HTTP請(qǐng)求
        char buffer[BUFSIZE];
        memset(buffer, 0, BUFSIZE);
        ssize_t bytesRead = recv(clientSocket, buffer, BUFSIZE - 1, 0);
        if (bytesRead == -1)
        {
            perror("HandleClient(): Error reading from client socket");
            closesocket(clientSocket);
            return;
        }

        // 解析請(qǐng)求,提取URL
        std::string request(buffer);
        std::string url = ExtractUrl(request);

        std::cout << "<" << url << ">" << std::endl;
        
        // Website Filter; User Filter ; Website phishing
        if (websiteDetector_.IsURLBlocked(url))
        {
            std::cout << "Url Blocked Success: " << url << std::endl;
        }else
        {
            std::string newUrl = websiteDetector_.IsURLPhishing(url);
            if (newUrl == "Phishing")
            {
                // 向服務(wù)端請(qǐng)求,向客戶(hù)端發(fā)送
                if( ServerToClient(url, clientSocket) )
                {
                    std::cout << "Transmit Success!" << std::endl;
                }else
                {
                    std::cout << "Transmit Fail!" << std::endl;
                }
            }else
            {
                std::cout << "Phishing" << std::endl;
                std::string locationResponse = std::string("HTTP/1.1 302 Found") + MY_CRLF + "Location: " + newUrl + MY_CRLF + MY_CRLF;
                std::cout << locationResponse << std::endl;
                send(clientSocket, locationResponse.c_str(), locationResponse.size(), 0);
            }
            
        }

        std::cout << "----------------------" << std::endl;
        // 關(guān)閉連接
        closesocket(clientSocket);
    }

    // 提取URL
    std::string ExtractUrl(const std::string &httpRequest)
    {
        std::string url;
        // Debug
        // std::cout << "ExtractUrl(): httpRequest = " << std::endl << httpRequest << std::endl;

        // 在HTTP請(qǐng)求中查找"GET ",通常URL緊隨其后
        size_t getPos = httpRequest.find("GET ");
        if (getPos != std::string::npos)
        {
            // 找到"GET "后,查找下一個(gè)空格,該空格之后是URL
            size_t spacePos = httpRequest.find(' ', getPos + 4);
            if (spacePos != std::string::npos)
            {
                url = httpRequest.substr(getPos + 4, spacePos - (getPos + 4));
            }
        }
        return url;
    }
    
    void ParseUrl(const std::string &url, std::string &host, std::string &path)
    {
        // 查找 URL 中的 "http://",并獲取其后的部分
        size_t httpPos = url.find("http://");
        if (httpPos != std::string::npos)
        {
            std::string urlWithoutHttp = url.substr(httpPos + 7); // 7 是 "http://" 的長(zhǎng)度
            // 查找 "/",分隔主機(jī)名和路徑
            size_t slashPos = urlWithoutHttp.find('/');
            if (slashPos != std::string::npos)
            {
                host = urlWithoutHttp.substr(0, slashPos);
                path = urlWithoutHttp.substr(slashPos);
            }
            else
            {
                // 如果沒(méi)有找到 "/",則整個(gè)剩余部分都是主機(jī)名
                host = urlWithoutHttp;
                path = "/";
            }
        }
        else
        {
            // 如果沒(méi)有 "http://" 前綴,則默認(rèn)協(xié)議為 HTTP,整個(gè) URL 都是主機(jī)名
            host = url;
            path = "/";
        }

        // Debug
        // std::cout << "url: " + url << std::endl;
        // std::cout << "host: " + host << std::endl;
        // std::cout << "path: " + path << std::endl;

    }
    int CreateServerSocket(const std::string &host)
    {
        // 域名解析
        addrinfo* result = NULL;
        addrinfo hints;

        ZeroMemory(&hints, sizeof(hints));
        hints.ai_family = AF_INET;  // 使用IPv4地址
        hints.ai_socktype = SOCK_STREAM;

        if (getaddrinfo(host.c_str(), "http", &hints, &result) != 0)
        {
            fprintf(stderr, "CreateServerSocket(): Failed to resolve the host: %s\n", host.c_str());
            return -1; // 返回-1表示連接失敗
        }

        // 創(chuàng)建Socket
        int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (serverSocket == -1)
        {
            fprintf(stderr, "CreateServerSocket(): create socket error: %s(errno: %d)\n", strerror(errno), errno);
            freeaddrinfo(result);  // 釋放內(nèi)存
            return -1; // 返回-1表示連接失敗
        }

        // 設(shè)置服務(wù)器地址信息
        struct sockaddr_in serverAddr;
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(80); // 設(shè)置端口號(hào)為80,可以根據(jù)需要修改
        serverAddr.sin_addr.s_addr = ((struct sockaddr_in *)(result->ai_addr))->sin_addr.s_addr;

        // 連接到原服務(wù)器
        if (connect(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
        {
            fprintf(stderr, "CreateServerSocket(): connect error: %s(errno: %d)\n",strerror(errno),errno);
            closesocket(serverSocket); // 在Windows中使用closesocket關(guān)閉套接字
            freeaddrinfo(result);  // 釋放內(nèi)存
            return -1; // 返回-1表示連接失敗
        }
        freeaddrinfo(result);  // 釋放內(nèi)存
        return serverSocket; // 返回連接成功的套接字描述符
    }

    bool ServerToClient(const std::string &url, int clientSocket)
    {
        // 解析URL,獲取主機(jī)名和路徑
        std::string host, path;
        ParseUrl(url, host, path);

        // 創(chuàng)建Socket連接到原服務(wù)器
        int serverSocket = CreateServerSocket(host);
        if (serverSocket == -1)
        {
            return FALSE; // 處理連接失敗的情況
        }  
        // 構(gòu)建HTTP請(qǐng)求
        std::string httpRequest = "GET " + path + " HTTP/1.1" + MY_CRLF + "Host: " + host + MY_CRLF + "Connection: close" + MY_CRLF;

        std::string date;
        bool sel;

        if (cache_.Check(url)) 
        {
            sel = false;
            date = cache_.GetDate(url);
            std::string cacheRequest = httpRequest + "If-Modified-Since: " + date + MY_CRLF + MY_CRLF;
            // 發(fā)送HTTP, 帶有If-Modified-Since 首部行
            if (send(serverSocket, cacheRequest.c_str(), cacheRequest.size(), 0) == -1)
            {
                perror("Error sending request to server");
                closesocket(serverSocket);
                return FALSE;
            }
            std::string cacheResponse;
            char cacheBuffer[HEADSIZE];
            ssize_t cacheBytesRead;
            cacheBytesRead = recv(serverSocket, cacheBuffer, HEADSIZE - 1, 0);
            std::string responseNotModified(cacheBuffer);

    
            // std::cout << "responseNotModified: " << responseNotModified << std::endl;

            if (IsResponseNotModified(responseNotModified) ) 
            {
                // std::cout << "304 Not Modified" << std::endl;
                sel = false;
            }else
            {
                cache_.ClearFileContent(url); //清空
                sel = true;
            }

        }else
        {
            sel = true;
        }
    
        if (sel == false) 
        {

            // std::cout << "cache hit!" << std::endl;
            // 接收緩存,轉(zhuǎn)發(fā)到客戶(hù)端
            char buffer[BUFSIZE];
            size_t start = 0;
            size_t bytesRead;
            while (1)
            {
                if (cache_.Get(url, buffer, start, bytesRead) == false)
                {
                    perror("Error sending response to client");
                }

                // std::cout << "bytesRead: " << bytesRead << std::endl;
                if (bytesRead == 0) break;

                if (send(clientSocket, buffer, bytesRead, 0) == -1)
                {
                    perror("Error sending response to client");
                    closesocket(serverSocket);
                    return FALSE;
                }
            }
           
        }else
        {
            httpRequest += MY_CRLF;
            // 發(fā)送HTTP請(qǐng)求到原服務(wù)器
            if (send(serverSocket, httpRequest.c_str(), httpRequest.size(), 0) == -1)
            {
                perror("Error sending request to server");
                closesocket(serverSocket);
                return FALSE;
            }

            // 接收原服務(wù)器的HTTP響應(yīng)
            char buffer[BUFSIZE];
            size_t start = 0;
            ssize_t bytesRead;
            while ((bytesRead = recv(serverSocket, buffer, BUFSIZE - 1, 0)) > 0)
            {
                buffer[bytesRead] = '\0';
                // 發(fā)送接收到的數(shù)據(jù)到客戶(hù)端
                if (send(clientSocket, buffer, bytesRead, 0) == -1)
                {
                    perror("Error sending response to client");
                    closesocket(serverSocket);
                    return FALSE;
                }
                if(cache_.Put(url, buffer, bytesRead, start) == false)
                {
                    std::cerr << "Cache put error" << std::endl;
                }
            }
            cache_.PutDate(url);
            if (! cache_.Check(url))
            {
                cache_.ClearFileContent(url);
            }
        }
        // 關(guān)閉原服務(wù)器連接
        closesocket(serverSocket);
        return TRUE;
    }
    bool IsResponseNotModified(const std::string& response) {
       // 查找第一個(gè)空格,定位到狀態(tài)碼的開(kāi)始
        size_t spacePos = response.find(' ');
        if (spacePos != std::string::npos) {
            // 提取狀態(tài)碼部分
            std::string statusCode = response.substr(spacePos + 1, 3);
            // 檢查狀態(tài)碼是否為 "304"
            return (statusCode == "304"); // HTTP/1.1 304 Not Modified
        }
        return false; // 未找到狀態(tài)碼
    }
};



bool InitWinsock()
{
    // 加載套接字庫(kù)(必須)
    WORD wVersionRequested;
    WSADATA wsaData;
    // 套接字加載時(shí)錯(cuò)誤提示
    int err;
    // 版本 2.2
    wVersionRequested = MAKEWORD(2, 2);
    // 加載 dll 文件 Scoket 庫(kù)
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0)
    {
        // 找不到 winsock.dll
        printf("加載 winsock 失敗,錯(cuò)誤代碼為: %d\n", WSAGetLastError());
        return FALSE;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("不能找到正確的 winsock 版本\n");
        return FALSE;
    }

    return TRUE;
}

int main()
{
    if (!InitWinsock())
    {
        WSACleanup();
        return -1; // 初始化失敗,退出程序
    }
    int port = 666; // 設(shè)置端口
    HttpProxyServer proxyServer(port);
    proxyServer.Start();
    WSACleanup(); // 在程序結(jié)束時(shí)清理Winsock庫(kù)
    return 0;
}

以上就是C++中HTTP 代理服務(wù)器的設(shè)計(jì)與實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ HTTP 代理服務(wù)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++使用boost::lexical_cast進(jìn)行數(shù)值轉(zhuǎn)換

    C++使用boost::lexical_cast進(jìn)行數(shù)值轉(zhuǎn)換

    這篇文章介紹了C++使用boost::lexical_cast進(jìn)行數(shù)值轉(zhuǎn)換的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • win10+VS2017+Cuda10.0環(huán)境配置詳解

    win10+VS2017+Cuda10.0環(huán)境配置詳解

    這篇文章主要介紹了win10+VS2017+Cuda10.0環(huán)境配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • C++編程異常處理中try和throw以及catch語(yǔ)句的用法

    C++編程異常處理中try和throw以及catch語(yǔ)句的用法

    這篇文章主要介紹了C++編程異常處理中try和throw以及catch語(yǔ)句的用法,包括對(duì)Catch塊的計(jì)算方式的介紹,需要的朋友可以參考下
    2016-01-01
  • Qt禁止程序多開(kāi)的實(shí)現(xiàn)示例

    Qt禁止程序多開(kāi)的實(shí)現(xiàn)示例

    本文主要介紹了Qt 禁止程序多開(kāi)的實(shí)現(xiàn)示例,主要介紹了三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • C++實(shí)現(xiàn)圖書(shū)信息管理系統(tǒng)

    C++實(shí)現(xiàn)圖書(shū)信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書(shū)信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C語(yǔ)言動(dòng)態(tài)內(nèi)存管理介紹

    C語(yǔ)言動(dòng)態(tài)內(nèi)存管理介紹

    大家好,本篇文章主要講的是C語(yǔ)言動(dòng)態(tài)內(nèi)存管理介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽
    2021-12-12
  • C語(yǔ)言數(shù)組全面總結(jié)梳理

    C語(yǔ)言數(shù)組全面總結(jié)梳理

    在C語(yǔ)言和C++等語(yǔ)言中,數(shù)組元素全為指針變量的數(shù)組稱(chēng)為指針數(shù)組,指針數(shù)組中的元素都必須具有相同的存儲(chǔ)類(lèi)型、指向相同數(shù)據(jù)類(lèi)型的指針變量。指針數(shù)組比較適合用來(lái)指向若干個(gè)字符串,使字符串處理更加方便、靈活
    2022-02-02
  • 基于QT和百度云api實(shí)現(xiàn)批量獲取PDF局部文字內(nèi)容

    基于QT和百度云api實(shí)現(xiàn)批量獲取PDF局部文字內(nèi)容

    這篇文章將為大家介紹如何使用 QT 構(gòu)建圖形用戶(hù)界面,結(jié)合百度云 OCR API 實(shí)現(xiàn)批量獲取 PDF 局部文字內(nèi)容并對(duì)文件進(jìn)行改名的功能,需要的可以參考下
    2025-03-03
  • C語(yǔ)言結(jié)構(gòu)體指針案例解析

    C語(yǔ)言結(jié)構(gòu)體指針案例解析

    這篇文章主要介紹了C語(yǔ)言結(jié)構(gòu)體指針案例解析,本文通過(guò)例子來(lái)解釋說(shuō)明了C語(yǔ)言的結(jié)構(gòu)體概念和如何用指針去操作結(jié)構(gòu)體,文章標(biāo)明了詳細(xì)的代碼,需要的朋友可以參考下
    2021-07-07
  • 常用排序算法的C語(yǔ)言版實(shí)現(xiàn)示例整理

    常用排序算法的C語(yǔ)言版實(shí)現(xiàn)示例整理

    這篇文章主要介紹了常用排序算法的C語(yǔ)言版實(shí)現(xiàn)示例整理,包括快速排序及冒泡排序等,基本上都給出了時(shí)間復(fù)雜度,需要的朋友可以參考下
    2016-03-03

最新評(píng)論