blog

高度に向かって:優れたAndroidプログラマは、ネットワークの基本を知っている必要があります知っている必要がある

ネットワーク通信は、Androidプロジェクトでは比較的重要なモジュールとなっている、Androidのオープンソースプロジェクトは、優れたネットワークフレームワークの多くに登場している、ツールやカプセ...

Nov 2, 2020 · 16 min. read
シェア

元記事へのリンク

この記事の最初の言葉は次のとおりです。

ネットワーク通信は、HttpClientとHttpUrlConnectionのちょうどいくつかの初めから、ツールクラスの使用をカプセル化するのは簡単優れたネットワークフレームワークの多くの出現のAndroidプロジェクト、Androidのオープンソースプロジェクトでは比較的重要なモジュールされている、後のGoogleのオープンソースより完全かつ豊富なボレーに、そして、より人気のある今日へ!より人気の、。

これらの間に存在する類似点と相違点を理解するためには、ネットワークの基礎、Androidネットワークフレームワークの基礎などを手元に置いて、重要な瞬間にアプリに最適なネットワーク通信技術のプラクティスを見つけられるようにする必要があります。

これは、Androidとソースコードの読書の日常的な開発でも、多くの場合、これらのネットワークの基礎を習得し、関連する知識に遭遇することが判明しただけでなく、Androidプログラマは本当に高レベルのプロセスに入るプロセスの一つの基本的な技術的資質のいくつかを持っている必要があります。

これを念頭に置いて、この記事では、コンピュータネットワークの基礎のいくつかに焦点を当て、Android開発で遭遇し、解決された用途や問題のいくつかを紹介します。

1) コンピュータ・ネットワーク・アーキテクチャ;

2) Http関連;

3) Tcp相関;

4) ソケット

Ctrip.comのAndroid開発エンジニア。

本記事を掲載するにあたり、内容をより理解しやすくするため、より慎重に修正しました。

コンピュータネットワークアーキテクチャ



上の図に示すように、上から下までの5つのレイヤーアーキテクチャーは、最終的にエンド・ツー・エンドのデータ伝送とその間の通信を実現することができ、それぞれのレイヤーは何の一部を担当し、最終的にどのようにエンド・ツー・エンドの通信を実現するのか?

このようなhttpプロトコルとして、それは実際にパッケージ化し、データを解析する方法を定義し、アプリケーション層は、httpプロトコルであり、それはプロトコルの規定に従って、要求行、要求ヘッダ、要求本文のパッケージに従ってなど、データをパッケージ化するためにされ、データの後にパッケージ化されたデータは、トランスポート層に渡されます。

トランスポート層は、TCPとUDPの2つのプロトコルは、信頼性の高いトランスポートと信頼性の低いトランスポートに対応するため、TCPなどの信頼性の高いトランスポートを提供する必要があるため、どのように接続を確立するための内部ソリューションは、どのように伝送が信頼性があり、データを失うことはありません、どのようにフロー制御と輻輳制御を調整します。この層については、通常、通常、ソケットを扱う、ソケットは、カプセル化されたプログラミングコールのインターフェイスのセットです、それを介して、TCPを操作することができますので、接続の確立のためのUDP。接続の確立のためのソケットの通常の使用は、一般的にポート番号を指定する必要がありますので、この層は、対応するポート番号に送信されるデータを指定します。

IPプロトコルのこのレイヤーは、いくつかのルーティング・プロトコルなどと同様に、データがどのIPアドレスに送信されるかを指定します。この間には最適ルートやルーティング・アルゴリズムなどがあります。

より印象的なのはARPプロトコルで、IPアドレスをMACアドレス、つまりハードウェアアドレスに解決し、対応する固有のマシンを見つける役割を担っています。

このレイヤーは最下層で、バイナリー・ストリーミング・サービス、つまり伝送媒体を介したデータ伝送の実際の開始を提供します。

MACアドレス - IPアドレス - ポート番号 - 物理的な伝送媒体を達成するために、それぞれの役割の上記の5つの層を介してだから、最終的にネットワーク通信とデータ伝送を達成するためにデータを解析するために、アプリケーション層のプロトコルに従ってデータを取得します。

以下は、HTTPとTCPに関連することに焦点を当てますが、他の層については、長いので、また、次の3つの層、ルーティングアルゴリズム、ARPアドレッシング、および物理層などのより詳細かつ具体的な理解になりたい場合は、多くを忘れて卒業するか、または再訪問するOkhttp"に。

HTTP

このセクションでは、Httpの基本的な部分と、Androidでの実用的なアプリケーション、そして遭遇した問題と解決策に焦点を当てます。

怠け者のためのWebプログラミング入門:HTTPプロトコルの徹底的かつ包括的な理解

TCP/IP詳細第1巻:プロトコル

HTTPの "コネクションレス "と "ステートレス "の理解

Httpはコネクションレスでステートレスです。

コネクションレスとは、接続する必要がないという意味ではありません。Httpプロトコルはあくまでアプリケーション層のプロトコルで、最終的にはTCPプロトコルのようなトランスポート層に依存し、接続するためのサービスを提供します。

ステートレスとは、各リクエストが互いに独立しており、リクエストトランザクションを記憶する機能を持たないことを意味します。クッキーが状態を保存するために使われるのはそのためです。

リクエスト・メッセージとレスポンス・メッセージ

ここでは、HTTPリクエストとレスポンスのメッセージフォーマットの基本を簡単に説明します。





1) Get は、すべてのリクエストパラメータを url の後に連結し、最終的にアドレスバーに表示します。一方 Post は、リクエストパラメータのデータをリクエストボディに入れ、アドレスバーには再び表示しません;

2) 渡されたパラメータの長さの制限。

1)については、確かにブラウザであればアドレスバーに個人情報を晒すのは良くないですが、アドレスバーという概念がないアプリ開発であれば、やはりpostやgetを選択する際の制約になるのではないでしょうか?

2)については、長さの制限はブラウザの制限であって、取得自体には関係ないはずですし、アプリ開発であればこれも無視できるかどうか。

HTTPのキャッシュ機構

なぜ私は以下のHttpキャッシュ機構を導入したい理由は、この作品をキャッシュするネットワークリクエストのOkhttpは、むしろVolleyや他のフレームワークのようなクライアントが再生するキャッシュ戦略の独自のセットを記述するのではなく、Httpキャッシュ機構の使用です。

Httpのキャッシュは、主に制御する2つのフィールドのヘッダで使用されます:つまり、Cache-controlとETagは、以下に説明します。

private: クライアントだけがキャッシュできます;

public:クライアントもプロキシもキャッシュできます;

max-age: キャッシュの有効期限;

no-cache:キャッシュされたデータを検証するために比較キャッシュの使用を要求します;

no-store: すべてのメモリはキャッシュされません。

max-age: キャッシュの有効期限が切れるまでの時間、その後に再リクエスト;

no-cacheは:このフィールドが開いている場合は、キャッシュされたデータを確認するために対照的なキャッシュを使用する必要性を示し、その後、最大年齢のキャッシュが無効になっていない場合でも、あなたはまだ、リソースが更新されたかどうかを確認するためにサーバーへの要求を開始する必要があるかどうか、データを再要求する必要があるかどうか、対照的なキャッシュを行う方法については、Etagの役割は、次のように言うことです。サーバーは、リソースが更新されていないことを確認する場合は、304を返し、ローカルキャッシュを取ることができる、更新がある場合は、最新のリソースを返します;

no-store: このフィールドがオンの場合、キャッシュはフェッチされません。

クライアントが最初の要求を送信すると、サーバーは、リソースの識別コードEtagの現在の要求を送信し、次の時間の要求は、クライアントは、サーバー上の識別コードEtagは、クライアントEtagと最新のリソースEtagの比較を行うために渡されるIf-None-Matchを介してヘッダになります、それが同じである場合、それはリソースが更新されていないことを意味し、304を返します。

キャッシュ制御とEtagの協力を通じてHttpのキャッシュ機構を実現します。HTTPキャッシュの知識の詳細については、記事で"脳死ネットワークプログラミング:HTTPプロトコルは、知識のいくつかを知っている必要があります"詳細な読書を行うには、関連する章の、あなたはそれを参照することができます。

HTTPクッキー

上記で述べたように、Httpプロトコルはステートレスであり、Cookieは一般的にドメイン(ドメインに属する)、パス、Expires(有効期限)および他の属性を含むCookieの状態の一部を記憶するためにローカルキャッシュで使用されます。サーバはレスポンスヘッダにset-cookiesを設定することで、クライアントのCookieに状態を書き込むことができます。次にクライアントがリクエストを開始するとき、そのクッキーを持ってくることができます。

クッキーに関して言えば、通常、アプリ開発だけを行っているのであれば、クッキーに遭遇する頻度は比較的低いですが、WebViewの要件に関与しているのであれば、クッキーに遭遇する可能性は高いです。

要件は次のようなものです。ロードされたWebViewのH5ページはログインする必要があるため、ネイティブページでログインした後、手動でWebViewクッキーにチケットを書き込む必要があります。その後、WebViewにロードされたH5ページは検証のためにCookie内のチケットをサーバーに運びます。

しかし、問題が発生しました:WebViewをデバッグChromeの検査を通じて、手動でクッキーを書き込むと、確かにすでに書き込まれていますが、リクエストを開始するときに、クッキーは、リクエストの検証の失敗をもたらし、その後、トラブルシューティングを通じて、WebViewの属性は、次のコードによって引き起こされるデフォルトで閉じている開いて設定することができますされていません:

CookieManager cookieManager = CookieManager.getInstance(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cookieManager.setAcceptThirdPartyCookies(mWebView, true); } else{ cookieManager.setAcceptCookie(true); }.

Https

Https = Http + Ssl、セキュリティの主な原則は、非対称暗号化アルゴリズムを使用することです。通常の対称暗号化アルゴリズムは安全ではありません。

安全な通信を可能にする非対称暗号化アルゴリズムの核心は、公開鍵で暗号化された情報は秘密鍵でしか復号化できず、秘密鍵で暗号化された情報は公開鍵でしか復号化できないということです。

サーバ側は、CA機関が発行する証明書を申請するために、公開鍵と秘密鍵は、証明書を取得するために、秘密鍵は、サーバ側自体にのみ知られており、公開鍵は、クライアントに渡すことができるような他の人に通知することができますので、サーバ側を介してクライアントは、公開鍵のデータの独自の伝送を暗号化し、サーバ側は、秘密鍵を使用して、このデータを復号化することができます。クライアントとして、この公開鍵で暗号化されたデータは、唯一の秘密鍵で復号化することができ、この秘密鍵は、サーバーでのみ使用可能ですので、データの伝送は安全です。

上記は、非対称暗号化アルゴリズムがどのようにデータを保護するかを簡単に説明したものですが、実際には、 Https のプロセスはそれよりもはるかに複雑です:

というのも、CA証明書が送信中にすり替えられる危険性があり、その場合、クライアントはサーバー証明書の正当性をどのように検証すれば、両者間の通信が正当なものであることを保証できるのかという問題があるからです;

もう1つは、非対称アルゴリズムはデータの安全性は確保できるものの、対称アルゴリズムに比べて効率が相対的に悪いため、データの安全性と効率の向上を両立させるために、どのように最適化するかということです。

証明書の発行局とバージョン、証明書の利用者、証明書の公開鍵、証明書の有効時間、証明書の電子署名のハッシュ値と署名のハッシュアルゴリズムなど。

クライアントは、サーバーから渡された証明書の正当性を検証するために、まず、デジタル署名のハッシュ値1で証明書を復号化するために取得した公開鍵を使用し、次に署名のハッシュアルゴリズムで証明書を使用してハッシュ値2を生成し、2つの値が等しい場合、それは証明書が正当であることを意味し、サーバー側は信頼することができます。

ところで、プロジェクト開発において、Android WebViewを使って会社のテストサーバーのWebページを読み込む際に、証明書の有効期限が切れていて、Webページが読み込まれない白い画面になってしまうという問題についてです。

解決策は、一時的にテスト環境でSSLエラーを無視することです、あなたがWebページを読み込むことができるように、もちろん、本番ではそうしないでください、1つは、セキュリティ上の問題が発生することです、1つは、googleのプレイは、監査を通過することはありませんする必要があります。

@Override publicvoidonReceivedSslError(WebView view, SslErrorHandler, SslError error) { if(ContextHolder.sDebug) { handler.proceed().; return; } super.onReceivedSslError(view, handler, error); }.

HTTPSに関するより詳細で包括的な知識については、「インスタント・メッセージング・セキュリティ:HTTPSの原則を理解する方法なら、1つの記事で十分です」をお読みください。

Http 2.0

Http2.0は、Http1.xに比べて大幅に強化されたプロトコルです。

http1.xは、テキストプロトコルであり、http2.0は、基本単位としてフレームにバイナリですが、バイナリプロトコルであり、フレームは、フレームに加えて、データが含まれていますまた、フレームのIDが含まれています:ストリーム識別子、つまり、フレームの識別は、どの要求に属しているので、ネットワーク伝送は非常に柔軟になっています;

大きな改善は、元のhttp1.x 1接続1要求の状況は、より多くの制限があり、また、効率性の問題だけでなく、複数の接続を確立するための消費などの多くの問題を発生させます。

http1.xは、効率の問題を解決するために、リソースをロードするための同時要求を起動しようとすることがありますが、同時要求の制限の同じドメイン名のブラウザは、最適化の手段は、一般的に別のドメイン名へのリソースの要求は、この制限を突破することです。

とhttp2.0サポート多重化は、この問題に対する良い解決策することができます、複数の要求がTCP接続を共有し、複数の要求が同時にこのTCP接続上で同時にすることができます、1つは、複数のTCP接続の確立の消費の問題を解決することです、1つはまた、効率の問題を解決します。

では、1つのTCPコネクションで複数のリクエストを同時にサポートする原理とは何でしょうか?基本的な原理は上記のバイナリフレーミングで、各フレームにはIDがあるため、異なるフレームに対する複数のリクエストを順番に並べずに同時に送信することができ、サーバ側では各フレームのIDに基づいて、対応するリクエストにソートされます。

主なアイデアは、ヘッダを圧縮することによってリクエストのサイズを小さくし、トラフィックの消費を減らして効率を向上させることです。というのも、すべてのリクエストはヘッダを持たなければならず、このヘッダのデータは通常1レイヤーであるという問題があるからです。

.

HTTP2については、「From HTTP/0.9 to HTTP/2: An Essay on Historical Evolution and Design Thinking of HTTP Protocol」をご覧ください。

TCP

TCPはコネクション指向で、信頼性の高いデータ転送を提供します。このレイヤでは、TCPは通常Socket APIを通じてコネクションの確立などを操作します。

接続を確立するための3つのハンドシェイク



初回:SNY=1を送信して、このハンドシェイクがコネクション確立の要求であることを示し、seqがクライアントの乱数Xを生成。

2回目:SNY=1,ACK=1を送信し、クライアントのack=seq+1に応答し、サーバも自身を表す乱数seq=Yを生成してクライアントに送信します。

3回目:ack=1。seq=クライアント乱数+1、ack=サーバー乱数+1。

まず第一に、それは2つのハンドシェイクが最も基本的であることは非常に明確である、最初のハンドシェイクは、CエンドがSエンドに接続要求メッセージを送信し、Sエンドは、彼らが成功のCエンドと接続することができます知っているSエンドを受信しますが、この時点でCエンドは、Sエンドがこのメッセージを受信するかどうかを知らないので、Sエンドは、彼らとSエンドに接続することができることを確認するために、応答後にメッセージを受信するために、SエンドのCエンドです。これは2番目のハンドシェイクです。

C側は、S側と接続できると確信した場合のみ、データの送信を開始することができます。そのため、2回のハンドシェイクは最低限必要です。

では、なぜ3回目のハンドシェイクが必要なのでしょうか?仮に3回目のハンドシェイクがなく、2回のハンドシェイクで接続が確立したとみなされたらどうなるでしょうか?

3番目のハンドシェークは、失敗した接続要求メッセージセグメントが突然サーバーに引き渡され、エラーが発生するのを防ぐためのものです。

Cエンドが発信した最初のネットワーク接続要求が、何らかの理由でネットワークノード内でストールし、その結果、接続が解放されたある時点まで遅延してSエンドに到達し、そのメッセージはとっくに期限切れになっていますが、この時点では、SエンドはまだこれをCエンドの接続確立要求の最初のハンドシェイクとみなし、Sエンドは2回目のハンドシェイクでCエンドに応答しています。

ハンドシェイクが2回しかない場合、この時点で接続は確立していますが、この時点ではCエンドは送信するデータを持っておらず、Sエンドはバカみたいに待つことになり、結果的にリソースを大きく浪費することになります。そのため、3回目のハンドシェイクが必要で、これはCエンドが再び少し応答した場合にのみ回避できます。

データセキュリティ - 第18章 TCPコネクションの確立と終了

TCPプロトコルを平易に理解する:理論的基礎

理論的な古典:TCPプロトコルの3つのハンドシェイクと4つの波の説明。

理論と実践のリンク:TCP 3ハンドシェイクと4ハンドシェイクのWiresharkパケットキャプチャと解析

ネットワークプログラミング入門:3つのハンドシェイクと4つのウェーブでTCPをアニメーションで解説。

4つのウェーブ切断



上記のビルド接続図を解析すれば、この図を理解するのはそれほど難しくないはずです。

なぜ、接続を確立するときよりも余分な波があるのですか?

ここで、サーバーのACK(クライアントへの返信)とFIN(終了)メッセージが同時に送信されるのではなく、最初にACKが送信され、次にFINが送信されることがわかりますが、これも非常に理解しやすく、クライアントが切断を要求したとき、この時点で、サーバーはデータの最後まで送信されていない可能性があるため、最初のACKが送信され、その後、FINの最後までデータが送信されるのを待ちます。

上記では、TCPがコネクションを確立し、切断するプロセスについて説明しましたが、TCPの最も重要な機能は、信頼性の高い伝送を提供することです。

理論的な古典:TCPプロトコルの3つのハンドシェイクと4つの波の説明。

理論と実践のリンク:TCP 3ハンドシェイクと4ハンドシェイクのWiresharkパケットキャプチャと解析

ネットワークプログラミング入門:3つのハンドシェイクと4つのウェーブでTCPをアニメーションで解説。

スライディングウィンドウプロトコル

スライディング・ウィンドウ・プロトコルは、TCPの信頼性の高い送信を保証するための基本です。なぜなら、送信ウィンドウは、確認応答フレームを受信した場合にのみ、ウィンドウを後方に移動して他のフレームを送信し続けるからです。

送信ウィンドウが3フレームの場合



最初の3つのフレーム[1,2,3]の送信ウィンドウの冒頭で、その後、最初の3つのフレームを送信することができ、一時的な背面は、確認メッセージの受信機から受信した[1]フレームが送信されたような、送信することはできません、その後、送信ウィンドウのみ1フレーム、[2,3,4]に送信ウィンドウに戻ってシフトすることができ、同じ唯一のフレームの送信ウィンドウ内で送信することができ、もう一度、類推。

インターフェイスウィンドウも送信ウィンドウと同じサイズを持っており、例えば受信ウィンドウが5フレームの場合、受信ウィンドウからはみ出したフレームは破棄されます。



Stop-and-waitプロトコル:すべてのフレームは、次のフレームを送信する前に確認メッセージを待つ必要があります。

後方Nフレームプロトコル:累積確認方式を採用し、受信側はNフレームを正しく受信した後、累積確認メッセージを送信側ウィンドウに送信し、Nフレームが正しく受信されたことを確認します。送信側が規定時間内に確認メッセージを受信しなかった場合、タイムアウトまたはデータが失われたとみなし、確認フレーム以降のすべてのフレームを再送します。デメリット:エラー番号以降のPDUは送信済みですが、再送する必要があり無駄が多い。

再送プロトコルの選択:エラーが発生した場合、エラーに関与するPDUのみを再送することで、伝送効率を向上させ、不要な再送を削減します。

送信ウィンドウと受信ウィンドウの間に送信効率と受信効率のミスマッチが生じると輻輳が発生するため、TCPはこの問題を解決するためにフロー制御と輻輳制御のメカニズムを備えています。

フロー制御と輻輳制御

フロー制御とは、通信経路上のトラフィックを制御することで、送信側が受信側からのフィードバックを受けて送信速度を動的に調整することでフロー制御の効果を得るもので、送信側の送信速度が受信側の受信速度を超えないようにすることが目的。

輻輳制御は、通信サブネットワーク全体のトラフィックを制御するグローバル制御です。

スロースタート+混雑回避

まずは古典的な写真から:



スロースタートの使用の開始は、つまり、輻輳ウィンドウが1に設定され、その後、輻輳ウィンドウが指数関数的にスロースタートのしきい値に成長し、その後、輻輳回避に切り替え、つまり、加算成長、ある程度まで成長するように、ネットワークの輻輳をもたらし、その後、輻輳ウィンドウが1に再減少され、つまり、再びスロースタート、同時に12の新しいスロースタートのしきい値を調整するように、などなど。

高速再送+高速リカバリー

高速再送:前述の再送メカニズムは、タイムアウトで受信側からの応答をまだ受信していない場合、再送を開始するまで待ちます。高速再送の設計思想は、送信側が受信側から重複したACKを3回受信した場合、メッセージセグメントが失われたと判断でき、この時、設定されたタイムアウトが来るまで再送開始を待たずに、失われたメッセージセグメントを即座に再送することで、再送の効率を向上させるというものです。

ファストリカバリー:上記の輻輳制御では、ネットワークが輻輳状態になると、輻輳ウィンドウを1に減らし、スロースタートで再スタートします。ファストリカバリーは、この問題を最適化するために設計されています。 ファストリカバリーでは、輻輳が発生すると、輻輳ウィンドウは1ではなく、新しいスロースタートのしきい値までしか減らされず、次の図に示すように、ネットワークが直接輻輳に入るようになり、相加的な増大を避けることができます:



高速再送信と高速回復は、輻輳制御のさらなる改善点です。

TCP/IP Explained - Chapter 21 - TCPタイムアウトと再送、わかりやすい - TCPプロトコルの深い理解:RTT、スライディングウィンドウ、輻輳処理。

ソケット

ソケットは、APIのTCP/UDP操作のセットであり、HttpURLConnectionやOkhttpのように、これは比較的低レベルのネットワーク要求の送信を含み、最終的には、もちろん、ソケットを介してネットワーク要求の接続を送信することであり、Volleyのように、Retrofitは、パッケージのより上位層であり、最終的に依存していますHttpURLConnectionまたはOkhttpに依存して、最終的な接続の確立とリクエストの送信を実行します。

Socketの使い方は簡単で、Socketを確立する両端、ServerSocketと呼ばれるサーバー側で、接続を確立します。

論文は、次のように要約されています

もちろん、これらの内容は、私が知っているだけであり、非常に重要なコンピュータ-ネットワークの基礎は、非常に多くのネットワークの基本を探索するために深い理解に行く必要があると思います。たくさん書いて、照合の独自の基盤のネットワークの並べ替えは、玉を誘致するためにレンガのように右の間違いがあるかもしれませんが、また、あなたの牛のためのアドバイスを惜しまないでください。



Read next

非同期タスクの SpringBootシリーズ @Asyncチュートリアル

クラスに対してこのアノテーションを記述した後、@Asyncアノテーションはクラス内に存在しないフィールドを無視します。 非同期実行の結果を取得するためにFutureを使用する場合、それがtrueであるかどうかにかかわらず、ブロッキングメソッドgetを呼び出すか、その両方...

Nov 2, 2020 · 8 min read