最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Java實(shí)現(xiàn)HTTP和HTTPS代理服務(wù)詳解

 更新時(shí)間:2024年04月30日 10:28:29   作者:cloudy491  
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)HTTP和HTTPS代理服務(wù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

HTTP代理和HTTPS代理介紹

簡(jiǎn)單來(lái)說(shuō)就是其他的服務(wù)器代替幫你訪問(wèn)HTTP或者HTTPS,當(dāng)你的網(wǎng)絡(luò)受限無(wú)法訪問(wèn)某些網(wǎng)站時(shí),但是你的代理服務(wù)器可以訪問(wèn)某些網(wǎng)站,這時(shí)候你就設(shè)置代理,通過(guò)代理服務(wù)器去訪問(wèn)某些網(wǎng)站。例如:你使用某個(gè)ip頻繁去爬網(wǎng)站信息時(shí),可能你的ip就會(huì)被封,這時(shí)候你可能就需要代理去切換ip了。

HTTP代理

首先我們簡(jiǎn)單用Java搭一個(gè)Socket服務(wù)端,打印一下HTTP代理請(qǐng)求報(bào)文。

public static void main(String[] args) {
    try {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("Server started, waiting for client...");
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("Client connected: " + socket.getInetAddress().getHostAddress());
            new Thread(()->{
                try(InputStream inputStream = socket.getInputStream()) {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                    while (reader.ready()){
                        System.out.println(reader.readLine());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

使用curl -x 127.0.0.1:8888 http://www.baidu.com 設(shè)置本地代理8888端口,代理訪問(wèn)百度。

然后看一下Java的控制臺(tái),可以看到代理請(qǐng)求過(guò)來(lái)的HTTP報(bào)文。

GET http://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
User-Agent: curl/8.4.0
Accept: */*
Proxy-Connection: Keep-Alive

下面是通火狐瀏覽器拿到的請(qǐng)求頭原始報(bào)文,可以發(fā)現(xiàn)代理HTTP報(bào)文和正常HTTP報(bào)文不同處:

第一行中間一個(gè)是http://www.baidu.com/,另一個(gè)是/。

一個(gè)是Proxy-Connection,另一個(gè)是Connection。

GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: BIDUPSID=52C8D6333DE11AAB7F6B193EE8925F3E; PSTM=1700558835; H_PS_PSSID=40300_40446_40080_60138_40463_60175; BAIDUID=52C8D6333DE11AAB7F6B193EE8925F3E:FG=1; BD_UPN=13314752; COOKIE_SESSION=8189645_0_5_4_2_3_1_0_4_3_31_1_41_0_56_0_1713792557_0_1713792501%7C5%230_0_1713792501%7C1; baikeVisitId=3cf47ba3-c45a-4e5c-bc50-1cef890ac1f1; BA_HECTOR=a02g018h000l2l250k2k8k043iu9l41j2u8gd1t; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598
Upgrade-Insecure-Requests: 1

根據(jù)上面的不同,我們只需要將代理的報(bào)文轉(zhuǎn)成正常的HTTP報(bào)文,在Java中添加下面兩個(gè)方法替換一下內(nèi)容。

//替換域名部分,將http://www.baidu.com 變成 /
private static String replaceDomain(String line) {
    String reqUrl = line.split("\s+")[1];
    //http://www.baidu.com/
    // 提取域名部分
    int endIndex = reqUrl.indexOf("/", 8); // 從第 8 個(gè)字符開(kāi)始搜索斜杠
    String domain = endIndex != -1 ? reqUrl.substring(0, endIndex + 1) : reqUrl;
    // 替換
    return line.replaceFirst(domain, "/");
}
//Connection部分,將Proxy-Connection 變成 Connection
private static String replaceConnection(String line) {
    String prefix = "Proxy-Connection: ";
    return line.replaceFirst(prefix, "Connection: ");
}

報(bào)文修改后,還得知道這個(gè)報(bào)文往那個(gè)地方發(fā)送,可以通Host字段提出域名和端口,代碼如下可以拿到Host。

private static Object[] getHeader(String str) {
    String hostPrefix = "Host: ";
    String contentLengthPrefix = "Content-Length: ";
    String host = null;
    Integer contentLength = null;
    String[] line = str.split("\r\n");
    for (String l : line) {
        if (l.startsWith(hostPrefix)) {
            host = l.substring(hostPrefix.length());
        } else if (l.startsWith(contentLengthPrefix)) {
            contentLength = Integer.parseInt(l.substring(contentLengthPrefix.length()));
        }
    }
    return new Object[]{host, contentLength};
}

有了Host我可以從里面拿到域名和端口,然后進(jìn)行Socket連接,然后把修改好的報(bào)文寫(xiě)入Socket中。

//獲取host
String address = (String) getHeader(sb.toString())[0];
//分割host和端口
String[] split = address.split(":");
String host = split[0];
int port = split.length > 1 ? Integer.parseInt(split[1]) : 80;
System.out.println(sb.toString());
//初始化socket連接
socket = new Socket(host, port);
//寫(xiě)入第一次請(qǐng)求的數(shù)據(jù)
socket.getOutputStream().write(sb.toString().getBytes());

之后創(chuàng)建一個(gè)線(xiàn)程去讀Socket返回的數(shù)據(jù),也就是百度那邊返回的數(shù)據(jù),拿到數(shù)據(jù)后再寫(xiě)回給代理的clientSocket。

//創(chuàng)建新線(xiàn)程執(zhí)行socket讀取
Socket finalSocket = socket;
new Thread(() -> {
    try {
        InputStream socketInputStream = finalSocket.getInputStream();

        byte[] socketBt = new byte[1024];
        int socketlen = -1;
        while ((socketlen = socketInputStream.read(socketBt)) != -1) {
            System.out.println(new String(socketBt, 0, socketlen));
            clientSocket.getOutputStream().write(socketBt, 0, socketlen);
        }
        socketInputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            finalSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

這樣就完成了一半,比較麻煩的是在每次連接讀取的第一個(gè)代理報(bào)文,你得判斷結(jié)束,GET請(qǐng)求可以通過(guò)\r\n\r\n,POST就得通過(guò)Content-Length去判斷了,總數(shù)據(jù)長(zhǎng)度 = Content-Length+ 數(shù)據(jù)中的\r\n\r\n位置后。下面代碼可能長(zhǎng)些,就是判斷每次連接的第一個(gè)代理報(bào)文結(jié)束,結(jié)束標(biāo)志要么用Content-Length或者\r\n\r\n,拿到第一個(gè)代理報(bào)文就可以創(chuàng)建Socket連接,之后的數(shù)據(jù)寫(xiě)入這個(gè)Socket就行了。

InputStream inputStream = clientSocket.getInputStream();
StringBuilder sb = new StringBuilder();
byte[] bt = new byte[1024];
int len = -1;
boolean isFirstLine = true;
int contentLength = -1;
while ((len = inputStream.read(bt)) != -1) {
    
    //第一次請(qǐng)求
    if (socket == null) {

        if (isFirstLine) {
            isFirstLine = false;

            String replace = replaceDomain(bt, len);
            
            //跳過(guò)HTTPS連接
            if (replace.startsWith("CONNECT")) {
                len = -1;
                break;
            }
            sb.append(replace);
        } else {
            sb.append(new String(bt, 0, len));
        }
        if (contentLength == -1) {
            Integer length = (Integer) getHeader(new String(bt, 0, len))[1];
            if (length != null) {
                contentLength = length;
            }
            int crlfcrlf = findCRLFCRLF(bt, len) + 4;
            contentLength -= (len - crlfcrlf);
        }
        //長(zhǎng)度到達(dá)結(jié)束
        if (contentLength == 0) {
            break;
        }
        if (sb.toString().endsWith("\r\n\r\n")) {
            break;
        }
    } else {
        //首行需要修改
        if (isFirstLine) {
            String replace = replaceDomain(bt, len);
            socket.getOutputStream().write(replace.getBytes());
        } else {
            socket.getOutputStream().write(bt, 0, len);
        }
    }
}

使用curl -x 127.0.0.1:8888 http://www.baidu.com 測(cè)試一下,發(fā)現(xiàn)正常響應(yīng)。

上面是簡(jiǎn)單的測(cè)試,還得用瀏覽器去測(cè)試,話(huà)說(shuō)找一個(gè)http的網(wǎng)站,也太難,我這里就用python的文件下載管理頁(yè)面。

if __name__ == '__main__':
    # 定義服務(wù)器的端口
    PORT = 8000
    # 創(chuàng)建請(qǐng)求處理程序
    Handler = http.server.SimpleHTTPRequestHandler
    # 設(shè)置工作目錄
    os.chdir("C:\Windows\Media")
    # 創(chuàng)建服務(wù)器
    with socketserver.TCPServer(("", PORT), Handler) as httpd:
        print(f"服務(wù)啟動(dòng)在端口 {PORT}")
        httpd.serve_forever()

使用火狐瀏覽設(shè)置代理。

搞定,瀏覽器和控制臺(tái)顯示正常,注意瀏覽器的地址不要寫(xiě)127.0.0.1,他是不走代理的。

HTTPS代理

HTTPS代理比HTTP代理簡(jiǎn)單多了,也是通過(guò)Socket服務(wù)端,使用curl -x 127.0.0.1:8888 https://www.baidu.com 控制臺(tái)可以拿到下面HTTPS的代理報(bào)文。

CONNECT www.baidu.com:443 HTTP/1.1
Host: www.baidu.com:443
User-Agent: curl/8.4.0
Proxy-Connection: Keep-Alive

我簡(jiǎn)單畫(huà)一下大致流程,瀏覽器通過(guò)代理把上面CONNECT報(bào)文發(fā)送Java應(yīng)用,Java應(yīng)用拿到報(bào)文,從報(bào)文中獲取到域名和端口,通過(guò)域名和端口連接百度服務(wù)器,連接成功后,回應(yīng)瀏覽器HTTP/1.1 200 Connection estabished告訴他這邊已經(jīng)連接好了,可以發(fā)送數(shù)據(jù)了,最后瀏覽器和百度服務(wù)器雙方交換數(shù)據(jù)。

瀏覽器Java應(yīng)用百度服務(wù)器CONNECT報(bào)文連接HTTP/1.1 200 Connection estabished發(fā)送數(shù)據(jù)寫(xiě)入數(shù)據(jù)瀏覽器Java應(yīng)用百度服務(wù)器

邏輯和HTTP一樣的,只是他不用把CONNECT報(bào)文發(fā)送到百度服務(wù)器上,就不用修改報(bào)文,而且結(jié)束只用通過(guò)\r\n\r\n判斷,和百度服務(wù)器連接上就回應(yīng)HTTP/1.1 200 Connection estabished,簡(jiǎn)單多了。

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

相關(guān)文章

  • SpringBoot配置開(kāi)發(fā)環(huán)境的詳細(xì)步驟(JDK、Maven、IDEA等)

    SpringBoot配置開(kāi)發(fā)環(huán)境的詳細(xì)步驟(JDK、Maven、IDEA等)

    文章介紹了如何配置SpringBoot開(kāi)發(fā)環(huán)境,包括安裝JDK、Maven和IDEA,并提供了詳細(xì)的步驟和配置方法,感興趣的朋友一起看看吧
    2024-12-12
  • 通過(guò)代碼示例了解submit與execute的區(qū)別

    通過(guò)代碼示例了解submit與execute的區(qū)別

    這篇文章主要介紹了通過(guò)代碼示例了解submit與execute的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 解決JDK9以上的非法反射訪問(wèn)警告的問(wèn)題

    解決JDK9以上的非法反射訪問(wèn)警告的問(wèn)題

    文章介紹了在JDK9及以上版本中,由于模塊系統(tǒng)限制,舊版庫(kù)(如protostuff)會(huì)出現(xiàn)非法反射訪問(wèn)的警告,解決方法是使用--illegal-access參數(shù),并通過(guò)--add-opens參數(shù)允許特定模塊的非法反射訪問(wèn),文章還提供了在IDEA和構(gòu)建工具(Maven和Gradle)中配置這些參數(shù)的示例
    2025-11-11
  • Spring Boot 中密碼加密的兩種方法

    Spring Boot 中密碼加密的兩種方法

    這篇文章主要介紹了Spring Boot 中密碼加密的兩種方法,幫助大家更好的理解和使用Spring Boot框架,感興趣的朋友可以了解下
    2020-09-09
  • fastjson轉(zhuǎn)換對(duì)象實(shí)體@JsonProperty不生效問(wèn)題及解決

    fastjson轉(zhuǎn)換對(duì)象實(shí)體@JsonProperty不生效問(wèn)題及解決

    這篇文章主要介紹了fastjson轉(zhuǎn)換對(duì)象實(shí)體@JsonProperty不生效問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java正則表達(dá)式使用示例

    java正則表達(dá)式使用示例

    這篇文章主要介紹了java正則表達(dá)式使用示例,實(shí)現(xiàn)拆分字符串、替換字符串、判斷字符串是否與制定模式匹配等功能,需要的朋友可以參考下
    2014-03-03
  • java對(duì)接webservice接口的4種方式總結(jié)

    java對(duì)接webservice接口的4種方式總結(jié)

    因工作需要和一個(gè)Sap相關(guān)系統(tǒng)以WebService的方式進(jìn)行接口聯(lián)調(diào),之前僅聽(tīng)過(guò)這種技術(shù),但并沒(méi)有實(shí)操過(guò),所以將本次開(kāi)發(fā)進(jìn)行記錄,這篇文章主要給大家介紹了關(guān)于java對(duì)接webservice接口的4種方式,需要的朋友可以參考下
    2023-10-10
  • 創(chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)

    創(chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)

    下面小編就為大家?guī)?lái)一篇?jiǎng)?chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Spring Boot 聲明式調(diào)用 Feign 從入門(mén)到精通實(shí)例詳解

    Spring Boot 聲明式調(diào)用 Feign 從入門(mén)到精通實(shí)例詳解

    這篇文章給大家介紹Spring Boot聲明式調(diào)用Feign從入門(mén)到精通實(shí)例詳解,本文全面介紹了Feign的使用和配置,涵蓋基礎(chǔ)和高級(jí)特性,以及在復(fù)雜場(chǎng)景中的應(yīng)用,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • springboot 文件上傳大小配置的方法

    springboot 文件上傳大小配置的方法

    本篇文章主要介紹了springboot 文件上傳大小配置的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10

最新評(píng)論

资中县| 洛南县| 南昌县| 周至县| 和顺县| 德江县| 临泉县| 泸西县| 延川县| 莲花县| 东台市| 合阳县| 南城县| 江津市| 万年县| 彭泽县| 顺平县| 潼关县| 肇东市| 木兰县| 临漳县| 万宁市| 和平县| 西平县| 深水埗区| 丹巴县| 乌审旗| 阿瓦提县| 井冈山市| 长泰县| 海盐县| 临汾市| 买车| 宁蒗| 武安市| 万全县| 武乡县| 克拉玛依市| 镇康县| 论坛| 志丹县|