Okhttp實現原理

OkHttp 是一個高效的 HTTP 客戶端庫,廣泛應用于 Android 和 Java 應用中。它提供了簡潔的 API,支持多種協議,如 HTTP/1.x 和 HTTP/2,并且內置了緩存和重試機制。下面是結合源碼分析的 OkHttp 的實現原理:

核心組件

OkHttp 的核心組件包括:

  • Call:這是?OkHttp?的主要接口,用于發起 HTTP 請求。一個?Call?對象代表一個 HTTP 請求。
  • Request:包含請求的所有信息,如 URL、HTTP 方法、請求頭和請求體。
  • Response:包含服務器響應的所有信息,如狀態碼、響應頭和響應體。
  • Dispatcher:管理?OkHttp?的并發請求和線程池。
  • ConnectionPool:管理 HTTP 連接的復用和釋放。
  • Interceptor:提供攔截器鏈,允許在請求發送前和響應接收后進行修改。

請求的發起和執行

當你調用 OkHttpClientnewCall() 方法創建一個 Call 對象,并調用 execute()enqueue() 方法時,請求的執行流程如下:

  1. 創建 Call 對象Call 對象被創建,它持有 Request 對象的引用,并通過 execute() 同步執行請求或通過 enqueue() 異步執行請求。

  2. 分發請求Call 對象會調用 Dispatcher 來管理請求的執行。如果使用 enqueue()Dispatcher 會在后臺線程中執行請求。

  3. 攔截器鏈:請求經過一系列攔截器的處理。OkHttp 的攔截器按順序執行,每個攔截器都可以修改請求或響應。攔截器鏈的最后一個攔截器是 RealInterceptorChain,它負責實際的網絡請求。

  4. 建立連接:如果需要,OkHttp 會建立到服務器的 TCP 連接。它會優先使用連接池中的現有連接,如果沒有合適的連接,則創建新的連接。

  5. 發送請求:將請求寫入到套接字中,等待服務器響應。

  6. 讀取響應:讀取服務器的響應,解析狀態碼、響應頭和響應體。

  7. 關閉連接:如果不需要保留連接,則關閉連接。否則,連接會被放回連接池。

  8. 處理響應:響應被傳遞給請求的回調方法或同步返回。

源碼分析

Call 的執行流程為例,看看 OkHttp 的執行流程:

Java
1// OkHttpClient.java
2public Response execute(Request request) throws IOException {
3    synchronized (this) {
4        if (closed) throw new IllegalStateException("closed");
5    }
6
7    // Create a new call.
8    Call call = new RealCall(this, request);
9    return call.execute();
10}
11
12// RealCall.java
13public Response execute() throws IOException {
14    // ...
15    RealConnection connection = getConnection();
16    // ...
17
18    // Execute the request.
19    long startNs = System.nanoTime();
20    try {
21        StreamAllocation streamAllocation = connection.newStream(false /* doNotRetry */, false /* isMultiplexed */);
22        // ...
23        return readResponse(streamAllocation, responseBuilder);
24    } finally {
25        streamAllocation.finishedReadingResponseBody();
26        // ...
27    }
28}

在這段代碼中,OkHttpClientexecute() 方法創建了一個 RealCall 實例,然后調用 RealCallexecute() 方法來執行請求。RealCallexecute() 方法會獲取或創建一個 RealConnection,然后通過這個連接發送請求,并讀取響應。

攔截器(Interceptors)

攔截器是 OkHttp 中非常重要的概念,它允許你在請求發送前和響應到達后添加自定義的邏輯。OkHttp 使用了一個攔截器鏈來組織多個攔截器的執行順序,這樣就可以在請求和響應的不同階段添加額外的行為。

Interceptor Chain

RealCall 的執行過程中,攔截器鏈通過 RealInterceptorChain 類實現。每一個 Interceptor 都可以訪問到請求和響應,并有機會修改它們。攔截器鏈中的最后一個攔截器是 NetworkInterceptor,它負責實際的網絡請求。

Java

1// RealInterceptorChain.java
2public Response proceed(Request request) throws IOException {
3    if (index == interceptors.size()) throw new AssertionError("no network interceptor");
4
5    // Call the next interceptor in the chain.
6    Interceptor interceptor = interceptors.get(index);
7    index++;
8
9    if (interceptor == networkInterceptor) {
10        // This is the last interceptor. Time to make the network call!
11        RealConnection connection = connect();
12        return withConnection(connection, new Callable<Response>() {
13            @Override public Response call() throws IOException {
14                return networkInterceptor.intercept(networkChain.proceed(request));
15            }
16        });
17    } else {
18        // Not the last interceptor. Proceed down the chain.
19        return interceptor.intercept(this);
20    }
21}

連接池(Connection Pool)

OkHttp 使用連接池來復用 HTTP 連接,這對于提高應用性能至關重要,尤其是對于需要頻繁發起網絡請求的場景。連接池中的連接可以被重復使用,從而避免了頻繁創建和銷毀連接所帶來的開銷。

ConnectionPool

ConnectionPool 是 OkHttp 中管理連接復用的核心組件。它會緩存空閑的連接,并在需要時提供給請求使用。當連接不再需要時,它們會被放回連接池,直到達到最大限制或連接過期。

Java
1// ConnectionPool.java
2public synchronized void put(Connection connection) {
3    if (connection == null) throw new NullPointerException("connection == null");
4    if (!connection.isEligibleForPool()) return;
5
6    String routeKey = connection.route().hostAddress();
7    Evictor<Connection> evictor = evictors.get(routeKey);
8    if (evictor == null) {
9        evictor = new Evictor<>(this, routeKey);
10        evictors.put(routeKey, evictor);
11    }
12    evictor.put(connection);
13}

重試機制

OkHttp 支持自動重試,可以在網絡不穩定或服務器暫時不可用的情況下自動重發請求。重試機制是通過攔截器實現的,可以配置重試次數和重試條件。

RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor 是 OkHttp 內置的一個攔截器,用于處理重試和重定向。它會根據網絡狀況和服務器響應的狀態碼決定是否重試請求。

Java

1// RetryAndFollowUpInterceptor.java
2public Response intercept(Chain chain) throws IOException {
3    Request request = chain.request();
4    int attemptNumber = 1;
5
6    while (true) {
7        Response response = chain.proceed(request);
8
9        if (attemptNumber > retryCount) break;
10
11        if (response.isSuccessful()) break;
12
13        // Decide whether to retry based on the response code and other conditions.
14        if (shouldRetry(response, attemptNumber)) {
15            // Close the socket before retrying.
16            response.close();
17            request = getNextRequest(response, request);
18            attemptNumber++;
19        } else {
20            break;
21        }
22    }
23
24    return response;
25}

緩存

OkHttp 還支持響應緩存,可以將服務器響應存儲在本地,以減少未來的網絡請求。緩存的實現涉及多個組件,包括 Cache 類和相關的攔截器。

Cache

Cache 類用于管理緩存文件和元數據。它遵循 HTTP 協議中的緩存控制規則,以決定哪些響應可以被緩存,以及何時重新驗證緩存的有效性。

Java
1// Cache.java
2public Response get(Request request) throws IOException {
3    // Check if we have a cached response that can be used.
4    // If so, return it.
5    // Otherwise, return null.
6}
7
8public void update(Response networkResponse, Response cacheResponse) throws IOException {
9    // Update the cache with the latest network response.
10}

TLS 和 SSL/TLS 協商

OkHttp 支持 HTTPS 協議,即安全的 HTTP 協議,這要求客戶端與服務器之間進行 SSL/TLS 握手。OkHttp 使用 Java 的 SSLSocketFactorySSLContext 來處理 TLS 連接的建立。

SSLSocketFactory 和 SSLSession

在 OkHttp 中,OkHttpClient.Builder 提供了 sslSocketFactory(SSLSocketFactory, X509TrustManager) 方法,允許開發者自定義 SSLSocketFactory 和信任管理器。這使得 OkHttp 能夠處理自簽名證書、過期證書或任何其他不被標準信任庫接受的證書。

Java
1// OkHttpClient.Builder.java
2public OkHttpClient.Builder sslSocketFactory(SSLSocketFactory sslSocketFactory,
3                                            X509TrustManager trustManager) {
4    if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
5    if (trustManager == null) throw new NullPointerException("trustManager == null");
6    this.sslSocketFactory = sslSocketFactory;
7    this.trustManager = trustManager;
8    return this;
9}

DNS 解析

OkHttp 支持多種 DNS 解析策略,包括標準的 DNS 解析、DNSSEC、IPv6 和多播 DNS。通過 Dns 接口,OkHttp 允許開發者自定義 DNS 解析行為。

Dns 接口

Dns 接口定義了 lookup 方法,該方法返回一個主機名的 IP 地址列表。OkHttp 默認使用系統的 DNS 解析器,但可以通過 OkHttpClient.Builderdns(Dns) 方法來替換。

Java
1// OkHttpClient.Builder.java
2public OkHttpClient.Builder dns(Dns dns) {
3    if (dns == null) throw new NullPointerException("dns == null");
4    this.dns = dns;
5    return this;
6}

超時

OkHttp 提供了全面的超時控制,包括連接超時、讀取超時和寫入超時。這些超時策略有助于處理網絡延遲和服務器響應緩慢的問題。

超時配置

通過 OkHttpClient.Builder,可以設置全局的超時策略,也可以在每個請求中單獨指定超時時間。

Java
1// OkHttpClient.Builder.java
2public OkHttpClient.Builder connectTimeout(int duration, TimeUnit unit) {
3    if (duration < 0) throw new IllegalArgumentException("duration < 0");
4    if (unit == null) throw new NullPointerException("unit == null");
5    this.connectTimeoutMillis = unit.toMillis(duration);
6    return this;
7}
8
9public OkHttpClient.Builder readTimeout(int duration, TimeUnit unit) {
10    if (duration < 0) throw new IllegalArgumentException("duration < 0");
11    if (unit == null) throw new NullPointerException("unit == null");
12    this.readTimeoutMillis = unit.toMillis(duration);
13    return this;
14}
15
16public OkHttpClient.Builder writeTimeout(int duration, TimeUnit unit) {
17    if (duration < 0) throw new IllegalArgumentException("duration < 0");
18    if (unit == null) throw new NullPointerException("unit == null");
19    this.writeTimeoutMillis = unit.toMillis(duration);
20    return this;
21}

HTTP/2 支持

OkHttp 自版本 3.0 開始支持 HTTP/2 協議,這是一種二進制、多路復用的 HTTP 協議,相比 HTTP/1.1 能夠顯著減少延遲和提高性能。

HTTP/2 連接

OkHttp 使用 Okio 庫來實現高效的 I/O 操作,包括 HTTP/2 的幀處理和流控制。在 HTTP/2 連接中,多個請求可以在同一個 TCP 連接上并行處理,從而避免了 HTTP/1.1 中的隊頭阻塞問題。

Java
1// RealConnection.java
2public synchronized StreamAllocation newStream(boolean doNotRetry, boolean isMultiplexed) throws IOException {
3    // ...
4    if (isMultiplexed && !supportsMultiplexing()) throw new ProtocolException("Multiplexing not supported");
5
6    // ...
7    return new StreamAllocation(this, doNotRetry, isMultiplexed);
8}

性能優化

OkHttp 通過多種技術來優化網絡請求的性能,包括:

  • 連接池:通過復用已有的 TCP 連接,避免了每次請求都要建立新連接的開銷。
  • HTTP 緩存:通過遵循 HTTP 緩存控制頭,減少不必要的網絡往返。
  • 壓縮:支持 HTTP 壓縮,減少傳輸的數據量。
  • DNS 緩存:緩存 DNS 解析結果,減少 DNS 查詢的時間。

流式上傳和下載

OkHttp 支持流式上傳和下載,這意味著在上傳或下載大數據時,數據可以分塊傳輸,而不是一次性加載到內存中。這對于處理大文件或長時間運行的請求尤其有用。

流式上傳

在流式上傳中,你可以使用 RequestBody 的子類 BufferedSink 來逐步寫入數據,而不是一次性創建一個完整的 RequestBody 對象。

Java
1// 創建一個流式上傳的 RequestBody
2RequestBody requestBody = new RequestBody() {
3    @Override
4    public MediaType contentType() {
5        return MediaType.parse("application/octet-stream");
6    }
7
8    @Override
9    public void writeTo(BufferedSink sink) throws IOException {
10        // 逐塊寫入數據
11        byte[] buffer = new byte[1024];
12        FileInputStream fis = new FileInputStream("largefile.dat");
13        int length;
14        while ((length = fis.read(buffer)) != -1) {
15            sink.write(buffer, 0, length);
16        }
17        fis.close();
18    }
19};
流式下載

流式下載允許你逐塊讀取響應體,而無需一次性加載整個響應到內存中。

Java
1// 創建一個流式下載的請求
2Request request = new Request.Builder()
3    .url("https://example.com/largefile")
4    .build();
5
6// 執行請求并逐塊讀取響應
7Response response = client.newCall(request).execute();
8ResponseBody body = response.body();
9if (body != null) {
10    BufferedSource source = body.source();
11    byte[] buffer = new byte[1024];
12    FileOutputStream fos = new FileOutputStream("output.dat");
13    long totalBytesRead = 0L;
14    int read;
15    while ((read = source.read(buffer)) != -1) {
16        totalBytesRead += read;
17        fos.write(buffer, 0, read);
18    }
19    fos.close();
20}

身份驗證

OkHttp 支持多種身份驗證機制,包括基本認證、摘要認證、OAuth 等。

基本認證
Java
1// 使用基本認證的請求
2String credentials = "username:password";
3String encodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
4Request request = new Request.Builder()
5    .url("https://example.com/api")
6    .header("Authorization", "Basic " + encodedCredentials)
7    .build();

跨域資源共享(CORS)

在處理跨域請求時,OkHttp 可以幫助你處理預檢請求(OPTIONS 請求),這是 CORS 的一部分,用于確定是否允許跨域請求。

處理預檢請求

在服務器端,你需要確保響應 OPTIONS 請求,并設置適當的 CORS 頭部,如 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers。在客戶端,OkHttp 會自動處理這些預檢請求,你只需要正常發起你的主請求即可。

日志記錄

OkHttp 提供了一個內置的日志攔截器,可以幫助你調試網絡請求和響應。

Java
1// 添加日志攔截器
2HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
3loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
4client = new OkHttpClient.Builder()
5    .addInterceptor(loggingInterceptor)
6    .build();

性能監控

為了確保 OkHttp 的性能,你可能需要監控網絡請求的指標,如響應時間、請求成功率和失敗原因。OkHttp 提供了多種方式來收集這些數據。

使用 Listeners

OkHttp 的 EventListener 允許你監聽網絡請求的生命周期事件,如請求開始、響應接收、連接建立等。這可以幫助你收集性能數據和調試網絡問題。

Java

1// 創建一個 EventListener 來監聽請求事件
2class MyEventListener extends EventListener {
3    @Override
4    public void callStart(Call call) {
5        // 請求開始
6    }
7
8    @Override
9    public void responseReceived(Call call, Response response) {
10        // 響應接收
11    }
12
13    // 更多事件...
14}
15
16// 添加 EventListener 到 OkHttpClient
17OkHttpClient client = new OkHttpClient.Builder()
18    .eventListenerFactory(() -> new MyEventListener())
19    .build();

最佳實踐

在使用 OkHttp 時,有一些最佳實踐可以幫助你構建更健壯、更高效的網絡層:

  • 使用連接池:始終使用連接池來復用連接,避免頻繁的連接建立和關閉。
  • 處理異常:確保你的代碼能夠優雅地處理網絡異常,如超時、中斷和服務器錯誤。
  • 緩存策略:合理使用緩存,遵循 HTTP 緩存控制頭,減少不必要的網絡往返。
  • 安全:始終使用 HTTPS,確保數據傳輸的安全。
  • 資源回收:確保在使用完?ResponseBody?或其他資源后調用?close()?方法,以避免內存泄漏。

通過深入理解 OkHttp 的這些特性和最佳實踐,你可以構建出既高效又可靠的網絡請求層,為你的應用程序提供堅實的基礎。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44088.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44088.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44088.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Swift 數據類型

Swift 數據類型 Swift 是一種強類型語言,這意味著在 Swift 中聲明的每個變量和常量都必須具有明確的類型。Swift 的類型系統旨在幫助開發者編寫清晰、安全的代碼。本文將詳細介紹 Swift 中的基本數據類型,包括整數、浮點數、布爾值、字符和字符串。 整數類型 Swift 提供了…

音頻語言學習領域數據集現狀、分類及評估

Audio Language Learning (Audio-Text Learning) 是一個新興的研究領域&#xff0c;專注于處理、理解和描述聲音。它的發展動力是機器學習技術的進步以及越來越多地將聲音與其相應的文本描述相結合的數據集的可用性。 Audio Language Models (ALMs) 是這個領域的關鍵技術&#…

MATLAB中的SDPT3、LMILab、SeDuMi工具箱

MATLAB中的SDPT3、LMILab、SeDuMi工具箱都是用于解決特定數學優化問題的工具箱&#xff0c;它們在控制系統設計、機器學習、信號處理等領域有廣泛的應用。以下是對這三個工具箱的詳細介紹&#xff1a; 1. SDPT3工具箱 簡介&#xff1a; SDPT3&#xff08;Semidefinite Progra…

基于QT開發的反射內存小工具

前言 最近項目需要需要開發一個反射內存小工具&#xff0c;經過2天的修修改終于完成了。界面如下&#xff1a; 功能簡介 反射內存指定地址數據讀取反射內存指定地址數據寫入反射內存指定地址數據清理十進制、十六進制、二進制數據相互轉換 部分代碼 void RfmMain::setWOthe…

SqlSugar-使用SqlSugar進行多數據庫操作

使用SqlSugar進行多數據庫操作主要涉及以下幾個步驟&#xff1a; 1. 配置數據庫連接 首先&#xff0c;你需要在項目的配置文件中&#xff08;如appsettings.json、web.config或app.config&#xff09;配置多個數據庫的連接字符串。每個連接字符串都對應一個不同的數據庫。 例…

攻防世界(PHP過濾器過濾)file_include

轉換過濾器官方文檔&#xff1a;https://www.php.net/manual/zh/filters.convert.php#filters.convert.iconv 這道題因為convert.base64-encode被過濾掉了&#xff0c;所以使用convert.iconv.*過濾器 在激活 iconv 的前提下可以使用 convert.iconv.* 壓縮過濾器&#xff0c; 等…

Win10安裝MongoDB(詳細版)

文章目錄 1、安裝MongoDB Server1.1. 下載1.2. 安裝 2、手動安裝MongoDB Compass(GUI可視工具)2.1. 下載2.2.安裝 3、測試連接3.1.MongoDB Compass 連接3.2.使用Navicat連接 1、安裝MongoDB Server 1.1. 下載 官網下載地址 https://www.mongodb.com/try/download/community …

【第28章】MyBatis-Plus之插件主體

文章目錄 前言一、MybatisPlusInterceptor 概覽1. 屬性2. InnerInterceptor 接口 二、使用示例1.Spring 配置2.Spring Boot 配置3 .mybatis-config.xml 配置 三、攔截忽略注解 InterceptorIgnore四、手動設置攔截器忽略執行策略五、本地緩存 SQL 解析總結 前言 MyBatis-Plus 提…

android 固定圖片大小

在Android中&#xff0c;固定圖片大小可以通過多種方法實現&#xff0c;這些方法主要涉及到ImageView控件的使用、Bitmap類的操作&#xff0c;以及第三方庫&#xff08;如Glide&#xff09;的輔助。以下是幾種常見的方法&#xff1a; 1. 使用ImageView控件 在Android的布局文…

利用docker容器安裝node,使用vue的開發環境

目錄 vue-app ├── docker-data │ ├── site │ ├── app ├── docker-compose.yaml └── deploy.sh docker-compose.yaml yaml文件執行 version: 3.8services:node:image: node:latestcontainer_name: vue-appports:- "8080:8080" # 宿主8080映射容器8…

系統服務綜合項目

要求&#xff1a; 現有主機 node01 和 node02&#xff0c;完成如下需求&#xff1a; 1、在 node01 主機上提供 DNS 和 WEB 服務 2、dns 服務提供本實驗所有主機名解析 3、web服務提供 www.rhce.com 虛擬主機 4、該虛擬主機的documentroot目錄在 /nfs/rhce 目錄 5、該目錄由 no…

如何保證語音芯片的穩定性能和延長使用壽命

要讓語音芯片保持穩定性能&#xff0c;首先需要深入理解其工作原理和內部構造。語音芯片&#xff0c;作為現代電子設備中的核心組件之一&#xff0c;承載著聲音信號的處理與輸出功能。為了確保其穩定運行&#xff0c;我們需要從多個方面進行細致的考慮和操作。? 1、避免長期高…

Windows系統MySQL的安裝,客戶端工具Navicat的安裝

下載mysql安裝包&#xff0c;可以去官網下載&#xff1a;www.mysql.com。點擊downloads 什么&#xff1f;后面還有福利&#xff1f; 下載MySQL 下載企業版&#xff1a; 下載Windows版 5點多的版本有點低&#xff0c;下載8.0.38版本的。Window系統。下載下面的企業版。不下載…

鄉鎮集裝箱生活污水處理設備處理效率高

鄉鎮集裝箱生活污水處理設備處理效率高 鄉鎮集裝箱生活污水處理設備優勢 結構緊湊&#xff1a;集裝箱式設計減少了占地面積&#xff0c;便于在土地資源緊張的鄉鎮地區部署。 安裝方便&#xff1a;設備出廠前已完成組裝和調試&#xff0c;現場只需進行簡單的連接和調試即可投入使…

[數字圖像處理]基礎知識整理(部分,持續更新)

程序中描述一副圖像&#xff0c;已知其橫向縱向的像素個數即可&#xff08;&#xff09; 灰度直方圖能反映一副圖像各個灰度級像素占圖像的面積比&#xff08;√&#xff09; 從程序編寫的角度看&#xff0c;描述一副圖像的基本屬性通常包括其分辨率&#xff0c;即圖像的寬度…

Docker鏡像和容器的管理

1 Docker鏡像管理操作 開啟鏡像加速 根據關鍵字查詢鏡像 下載查看鏡像 詳細鏡像信息 查看latest版本 上傳鏡像到阿里云倉庫 2 Docker容器操作 關于容器根據第一個pid進程是否能正常在前臺運行

19. 地址轉換

地址轉換 題目描述 Excel 是最常用的辦公軟件。每個單元格都有唯一的地址表示。比如&#xff1a;第 12 行第 4 列表示為&#xff1a;"D12"&#xff0c;第 5 行第 255 列表示為"IU5"。 事實上&#xff0c;Excel 提供了兩種地址表示方法&#xff0c;還有一…

算法訓練營第30天|122.買賣股票的最佳時機II|55. 跳躍游戲|45.跳躍游戲II|1005.K次取反后最大化的數組和

122.買賣股票的最佳時機II 思路&#xff1a;只有前一天與后一天的利潤為正時&#xff0c;才將其加入總利潤。 55. 跳躍游戲 思路&#xff1a;找最大覆蓋范圍 出錯點&#xff1a;數組的遍歷&#xff0c;遍歷范圍應該是覆蓋范圍內 45.跳躍游戲II 思路&#xff1a; 局部最優&am…

批量爬取B站網絡視頻信息

使用XPath爬取B站視頻鏈接等相關信息 分析B站html框架獲取內容完整代碼 對于B站&#xff0c;目前網上的爬蟲大多都是使用通過解析服務器的響應來爬取想要的內容&#xff0c;下面我們通過使用XPath來爬取B站上一些想要的信息 此次任務我們需要對B站搜索到的關鍵字&#xff0c;并…

數據結構 —— FloydWarshall算法

數據結構 —— FloydWarshall算法 FloydWarshall算法三種最短路徑算法比較1. Dijkstra算法2. Bellman-Ford算法3. Floyd-Warshall算法總結 我們之前介紹的兩種最短路徑算法都是單源最短路徑&#xff0c;就是我們要指定一個起點來尋找最短路徑&#xff0c;而我們今天介紹的Floyd…