[ネットワーク原理の一つ] アプリケーション層プロトコル、トランスポート層プロトコル UDP と TCP、TCP の 3 ウェイ ハンドシェイクと 4 ウェイ ウェーブ、TCP の信頼性と効率性のメカニズム

アプリケーション層プロトコル

実装された TCP サーバーおよびクライアントでは、送信側によって決定されたアプリケーション層プロトコルにより、各メッセージの末尾に改行文字が使用されます。つまり、送信時は改行文字に応じてエンコードされ、受信時は改行文字に応じてデコードされます。アプリケーションプログラムを開発する際、契約内容を決めるのは大きな仕事です。一般的なアプリケーション層プロトコルには、HTTP、FTP などがあります。

XMLプロトコル

主にデータを整理するためのフォーマットです。XML ファイルでは、各タグはペアで表示され、終了タグには / が付いています。タグにサブタグが含まれている場合、このタグはオブジェクトを表すことができます。ラベルに複数の同一のサブラベルが含まれる場合、このラベルはコレクションを表します。

ここに画像の説明を挿入

短所: 構造が複雑、見苦しい、冗長な文字が多すぎる、ネットワーク内の送信により多くの帯域幅が消費されます。

JSON

1. オブジェクトを表すには {} を使用します。
2. コレクションを表すには [] を使用します。
3. 属性には "key": "value" を使用します。値が整数の場合、引用符は必要ありません;
4. 複数の属性はカンマで区切られ、最後の属性にはカンマが追加されません。

ここに画像の説明を挿入

JSON 形式の利点は、読みやすさ、美しさ、拡張性の高さですが、欠点は追加の文字が導入され、多くの帯域幅を消費することです。

HTTP

HTTP プロトコルについては、後の研究で取り上げます。

トランスポート層プロトコル

コア プロトコル
UDP: コネクションレス、信頼性の低い伝送、データグラム指向、全二重、サイズ制限あり。
TCP: 接続、信頼性の高い伝送、バイト ストリーム指向、全二重、サイズ無制限。

UDPプロトコル

UDPの特徴

1.接続なし: UDP 送信のプロセスはテキスト メッセージの送信と似ています。相手の IP とポート番号がわかっている場合は、接続を確立せずに直接送信できます。
2.信頼性の低い送信: セキュリティ機構がないため、送信者がデータグラムを送信した後、ネットワーク障害によりセグメントを相手に送信できない場合、UDP プロトコル層はアプリケーション層にエラー メッセージを返しません。
3.データグラム指向: UDP はアプリケーション層から送信された長期メッセージを分割や結合せずにそのまま UDP に送信します。

UDP を使用して 100 バイトのデータを送信する: 送信者が一度に 100 バイトを送信する場合、受信者も一度に 100 バイトを受信する必要があります。ループで 10 回、毎回 10 バイトを受信する必要はありません。

4.バッファ
• UDP には受信バッファのみがあり、送信バッファはありません;
• UDP には実際の送信バッファはありません。送信されたデータはカーネルに直接渡され、カーネルは後続の送信アクションのためにデータをネットワーク層プロトコルに渡します; • UDP には
受信バッファがありますが、この受信バッファは受信した UDP メッセージの順序を保証できません。送信される UDP メッセージの順序は一貫しています。バッファがいっぱいの場合、受信した UDP データは破棄されます。
• UDP ソケットは読み取りと書き込みの両方が可能であり、この概念は全二重と呼ばれます。

5. サイズ制限:
UDP プロトコル ヘッダーの最大長は 16 ビットです。つまり、UDP で送信できるデータの最大長は 64K (UDP ヘッダーを含む) です。

UDPプロトコル形式

ここに画像の説明を挿入

UDPはトランスポート層プロトコルであり、トランスポート層プロトコルはオペレーティングシステムによって実装され、オペレーティングシステムがプロセスを管理し、各プロセスがポート番号を開きます。16 ビットは最大 65535 を表すことができ、ポート番号の範囲が 0 ~ 65535 であることを示します。16 ビット UDP の長さは 65535 バイトで、これは約 64KB に相当します。UDP のチェックサムは CRC 冗長性チェックです。つまり、データ(バイト配列)の各バイトを累積した値です。UDP メッセージを解析するとき、最初の 16 ビットは送信元ポートを示し、その後 16 ビットを切り取って宛先ポート番号を識別します。最終的にインターセプトされるデータの長さは、UDP の長さによって決まります。

バイト蓄積の例:

public class Demo_CRC {
    
    
    public static void main(String[] args) throws UnsupportedEncodingException {
    
    
        // 定义两个字符串
//        String str = "你好世界";
        String str = "你好啊,一会去吃火锅吧!!!";
        String abc = "how are you.";
        // 转换成byte数组
        byte[] bytes = str.getBytes("UTF-8");
        System.out.println(Arrays.toString(bytes));
        System.out.println(bytes.length);

        // 循环累加每个byte的值,得到CRC结果
        int crc = 0;
        for (int i = 0; i < bytes.length; i++) {
    
    
            crc += bytes[i];
        }
        System.out.println("str crc = " + crc);

        // 转换成byte数组
        bytes = abc.getBytes("UTF-8");
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));
        // 循环累加每个byte的值,得到CRC结果
        crc = 0;
        for (int i = 0; i < bytes.length; i++) {
    
    
            crc += bytes[i];
        }
        System.out.println("acb crc = " + crc);
    }
}

TCPプロトコル

TCPの特徴

1接続: TCP 送信のプロセスは、通話のすべての側面と同様です
2信頼性の高い送信: TCP 自体のさまざまなメカニズムを通じて、信頼性の高い送信を保証
ますバッファ: TCP には受信バッファと送信バッファがあります。全二重。5.無制限のサイズ

TCPプロトコル形式

ここに画像の説明を挿入

16 ビットの送信元ポートと宛先ポートはUDP と同じで、プロセスを識別するために使用されます。
4 ビットのヘッダー長: 1111 = 15。ヘッダーには合計 15*4byte=60 バイトを含めることができます。前のオプションには 4 * 5 = 20 バイトがあるため、このオプションの最大値は 40 バイトです。
データは、アプリケーション層によって送信される負荷です。
6 つのフラグ: URG: 緊急ポインタが有効かどうか。ACK: 確認応答番号が有効かどうか。PSH: 受信側アプリケーションに、TCP バッファからデータをすぐに読み取るように指示します。RST: 相手は接続の再確立を要求します。RSTフラグを持つセグメントをリセット セグメントと呼びます。SYN: 接続確立の要求: SYN 識別子を同期セグメントと呼びます。FIN: ローカルエンドが閉じられることを相手に通知します。FINフラグを持つエンドセグメントを呼びます。
16ビットチェックサム:CRCチェックサム。
オプションはカスタム メッセージです。
16ビット緊急ポインタ:データのどの部分が緊急データであるかを識別しますが、当面は関係ありません。
シーケンス番号、確認シーケンス番号、ウィンドウサイズについては後ほど紹介します。

TCPの安全性と効率性の仕組み

確認応答(信頼性の高いメカニズム)

人とチャットする際に送受信するのが確認応答です。ネットワークの都合により、情報の送受信に乱れが生じる場合がございます。この問題を解決するために、TCP はデータの各バイトに番号を付けます。はシリアル番号です。

ここに画像の説明を挿入

このシーケンス番号は、前述の32ビットシーケンス番号および32ビット確認シーケンス番号に格納される。データの送受信のために、TCP はマークする SYN (送信) と ACK (応答) を提供します。ACK には確認シーケンス番号が含まれており、これは送信者にどこで受信したか、次回どこから送信を開始するかを通知します。リクエスト送信時はSYNフラグを1に、応答時はACKフラグを1にセットします。
ここに画像の説明を挿入

タイムアウト再送(信頼性の高い仕組み)

ネットワーク内での送信の過程で、メッセージはオペレーティング システム、ネットワーク カード、スイッチ、ルーター、その他のネットワーク デバイスを通過します。各デバイスには独自の負荷容量があり、その範囲を超えると、現在のデータ パケットがブロックされるか破棄される可能性があります。

1. 送信者がパケットを失う

しばらく待って ACK を受信して​​いないことが確認された場合は、指定した時間後に前のデータを再送信します。

2. 応答タイムアウト

ホスト B はデータを受信し、ACK 応答を送信しましたが、ホスト A は応答を受信しませんでした。この場合、二重受信の問題が発生します。このとき、ホスト B は、自身のバッファー内の 32 ビットの確認応答シーケンス番号を介して重複データをフィルターで除外します。そして直接ACK応答を返します。

ここに画像の説明を挿入

では、タイムアウト時間かどうかを判断するにはどうすればよいでしょうか?

• 理想的には、「この時間内に確認応答を返さなければならない」ことを保証するための最小限の時間を見つけます。
• ただし、この時間の長さはネットワーク環境によって異なります。
• タイムアウトの設定が長すぎると、全体的な再送効率に影響します; タイムアウトの設定が短すぎると、繰り返しパケットが頻繁に送信される可能性があります; • TCP はどのような環境でも高性能の通信を保証するため、最大タイムアウト期間
は動的に計算されます。Linux (BSD Unix および Windows も同様) では、タイムアウトは 500 ミリ秒単位で制御され、タイムアウト再送信ごとのタイムアウト時間は 500 ミリ秒の整数倍になります。再送信しても応答がない場合は、2 500ms待ってから再送信してください。それでも応答がない場合は、再送信するまで 4 500 ミリ秒待ちます。などなど、指数関数的に増加していきます。
• TCP は再送回数が一定回数蓄積すると、ネットワークまたは相手ホストが異常であると判断し、強制的にコネクションを切断します。

接続管理(信頼できる仕組み)

ホストが送信者および受信者としてネットワーク上で通信する場合、ホストは両方の当事者がデータを送受信できることを確認する必要があります。これには、接続の確立と切断のネゴシエーション プロセスが含まれます。高速鉄道は毎日始発前に空車で運行されますが、ネットワーク通信においては送信側と受信側の能力を確認するためのものです。

スリーウェイハンドシェイク(接続処理)

2回のSYNとACKの処理により、双方のネットワークに問題がないことが保証されます。これにより、通常のデータ送受信が可能となる。TCP 自体が効率を最適化し、SYN+ACK を 1 つの操作 ( 3 ウェイ ハンドシェイク)に結合します。
ここに画像の説明を挿入

完全な検証は存在しないため、2 つのハンドシェイクを使用して双方の送受信能力を確認することはできません。SYN+ACKを逆アセンブルするだけなので4回でも大丈夫です。

スリーウェイ ハンドシェイクのもう 1 つの重要な機能は、シリアル番号の開始位置をネゴシエートすることです

ここに画像の説明を挿入

ポートステータス

netstat-an コマンドを使用してポートを確認します。

ここに画像の説明を挿入

4回手を振った(切断プロセス)

ここに画像の説明を挿入

最初の ACK はオペレーティング システムによって実装された TCP プロトコルの応答であり、2 番目の FIN はアプリケーション レベルです。この2 つの操作には時間差があり、結合して返されない可能性が高いため、4 つの手を振ると表現されます。2 回目の FIN パケット損失にどう対処するか? パケットが失われた場合、タイムアウト再送信がトリガーされます。

状態遷移

ここに画像の説明を挿入

サーバ:

[CLOSED -> LISTEN]サーバーは listen を呼び出した後、LISTEN 状態に入り、クライアントの接続を待ちます。
[LISTEN -> SYN_RCVD]接続要求 (同期セグメント) を監視したら、接続をカーネル待機キューに入れ、SYN 確認メッセージをクライアントに送信します。
[SYN_RCVD -> ESTABLISHED]サーバーがクライアントから確認メッセージを受信すると、ESTABLISHED 状態になり、データの読み取りと書き込みが可能になります。
[ESTABLISHED -> CLOSE_WAIT]クライアントがアクティブに接続を閉じる (close を呼び出す) と、サーバーは終了メッセージ セグメントを受信し、サーバーは確認メッセージ セグメントを返して CLOSE_WAIT に入ります。CLOSE_WAIT を入力した後、サーバーが接続を閉じる準備ができていることを示します (前
[CLOSE_WAIT -> LAST_ACK]のデータを処理する必要がある); サーバーが実際に接続を閉じるために close が呼び出されるとき、FIN がクライアントに送信されます。この時点で、サーバーは LAST_ACK 状態に入り、最後の ACK が到着するのを待ちます (この ACKこれは、FIN が受信されたことを示すクライアントの確認です)。システム内に多数の CLOSE_WAIT 状態がある場合は、プログラムが close() メソッドを呼び出していない可能性があります。
[LAST_ACK -> CLOSED]サーバーは FIN への ACK を受信し、接続を完全に閉じます。閉じた後、システムがリソースを再利用するまで待機します。

クライアント:

[CLOSED -> SYN_SENT]クライアントは connect を呼び出して同期セグメントを送信します。connect
[SYN_SENT -> ESTABLISHED]呼び出しが成功すると、ESTABLISHED 状態になり、データの読み取りと書き込みを開始します。
[ESTABLISHED -> FIN_WAIT_1]クライアントがアクティブに close を呼び出すと、終了セグメントをサーバーに送信し、同時に FIN_WAIT_1 に入ります。 time
[FIN_WAIT_1 -> FIN_WAIT_2]; 終了セグメントの確認後、FIN_WAIT_2 を入力し、サーバーの終了セグメントの待ちを開始します; [FIN_WAIT_2 -> TIME_WAIT] クライアントはサーバーから終了セグメントを受信し、TIME_WAIT を入力し、LAST_ACK を送信します。
[TIME_WAIT -> CLOSED]wait 2MSL (最大セグメント寿命、メッセージの最大寿命) 時間が経過すると、CLOSED 状態になります。

スライディングウィンドウ(効率機構)

データの送受信のプロセスにより、正常な通信が保証されますが、効率は高くありません。この送受信方法のパフォーマンスは低いため、図に示すように、一度に複数のデータを送信します。

ここに画像の説明を挿入

1. 図
• スライディング ウィンドウ自体は、ウィンドウのサイズと送信済みおよび送信中のデータを維持するために使用されるデータ構造です。
• 白枠内のデータは ACK 待ちのデータセグメントです。
• ウィンドウ サイズとは、確認応答を待たずにデータを送信し続けることができる最大値を指します。上図のウィンドウ サイズは 4000 バイト (4 セグメント) です。
• 最初の 4 つのセグメントを送信するときは、ACK を待たずに直接送信します;
• 最初の ACK を受信した後、スライディング ウィンドウは逆方向に移動し、5 番目のセグメントのデータを送信し続けます; など ;
このスライディング ウィンドウを維持するには、現在応答のないデータを記録するために送信バッファを作成する必要がある; 確認され応答されたデータのみをバッファから削除できる;
• ウィンドウが大きいほど、ネットワークのスループットが高くなります。
ここに画像の説明を挿入

2. 予測可能なパケット損失の問題

ACK 応答が失われます。

特定の ACK が途中で失われた場合でも、最後の ACK 応答の確認シーケンス番号は、以前のすべてのデータ パケットが受信されたことを示します。

ここに画像の説明を挿入

SYN リクエストが失われました:

データを受信する過程で、32 ビットのシーケンス番号の一部が欠落していることが判明した場合、送信者にデータの欠落部分を問い合わせる ACK を常に送信します。このとき、受信した他のデータはキャッシュされ、データが完成した後に不足しているデータが組み立てられます。

ここに画像の説明を挿入

3. スライディングウィンドウの効率

効率はウィンドウのサイズに依存します。
ウィンドウが大きいほど効率は高くなります。
ウィンドウが小さいほど効率は低くなります。ウィンドウ
が無限であると仮定すると、送信者は ACK をまったく待つ必要がなく、効率は UDP と同じです。

フロー制御(信頼性の高い機構)

スライディングウィンドウの効率については前述しましたが、スライディングウィンドウの大きさはどれくらいですか? フロー制御は主にスライディング ウィンドウのサイズを確認します。これは、送信側と受信側の間の動的なネゴシエーションによって確認されます。例えば、ご飯を炊いた後、どのくらい食べられるか聞いて、好きなだけご飯をあげます。

1. 送受信バッファ

ここに画像の説明を挿入

各プログラムは起動時にシステム リソースを申請し、送信バッファと受信バッファは要求されたリソース、つまり BYTE データ ストリームを格納するために使用されるメモリ内の領域です。ACK は、ウィンドウ サイズ プロトコル フィールド (16 ビット ウィンドウ サイズ) にバッファー内の残りのスペースのサイズを埋めます。受信者は送信者のウィンドウ サイズ制限に対抗するため、送信者は効率を向上させるために無制限にウィンドウ サイズを拡張することはできません。使用済みスペースと残りのスペースのサイズは動的であり、受信側がバッファからデータを読み取るたびに、残りのスペースが大きくなります。

2. 具体的なプロセス

• 送信者は受信者にデータを送信します;
• データを受信した後、受信者は受信者のバッファー (メモリ上に開かれた空間) にデータを格納します; • 受信者のアプリケーションはソケット
API (lnputStream) を介してバッファーから読み取ります データを読み取るとき、バッファ内のデータは少なくなりますが、これはデータの受信者が ACK を受け取ったときにバッファの残りのスペースを送信者に送信するのと同じです; • 残りのスペースのサイズは、受信側がバッファ サイズを設定するのと同等です
。 TCP ヘッダーの「ウィンドウ サイズ フィールド」に受信でき、ACK を通じて送信側に通知します; • ウィンドウ サイズ フィールドが大きいほど、ネットワークのスループットが高くなります
。 , ウィンドウサイズを小さい値に設定し、送信者に通知します; •
ウィンドウを受信した後、送信者は送信速度を遅くします. いっぱいになるとウィンドウは 0 に設定されます; このとき、送信者ははデータを送信しなくなりましたが、受信側が送信側にウィンドウ サイズを通知できるように、ウィンドウ検出データ セグメントを定期的に送信する必要があります。

受信側の処理能力が低い場合、バッファがいっぱいになる可能性があります。このとき、送信者は実際のデータなしでウィンドウ検出リクエストを時々送信し、受信者にどれだけ受信できるかを尋ねます。
ここに画像の説明を挿入

3. 実際のウィンドウサイズ

TCP ヘッダーには、ウィンドウ サイズ情報を格納する 16 ビットのウィンドウ フィールドがあります。16 桁の最大値は 65535 なので、TCP ウィンドウの最大値は 65535 バイトですか? 実際、TCP ヘッダーの 40 バイト オプションにはウィンドウ拡張係数 M も含まれており、実際のウィンドウ サイズは、M ビットだけ左にシフトされたウィンドウ フィールドの値です

輻輳制御(確実な仕組み)

ネットワーク内のデータ送信プロセスは非常に複雑で、スイッチやルーターなどの多くのネットワーク デバイスを通過する場合があります。すべてのネットワーク デバイスに問題があると、伝送に影響します。

ここに画像の説明を挿入

TCP では、スロー スタートメカニズムが導入されています。このメカニズムでは、最初に少量のデータを送信し、パスを探索し、現在のネットワークの輻輳状態を調べてから、データを送信する速度を決定します。
ここで導入される概念は、輻輳ウィンドウと呼ばれます。

1. 送信開始時の輻輳ウィンドウ サイズは 1 として定義され、ACK 応答を受信するたびに輻輳ウィンドウ サイズは 1 ずつ増加します。 2. 次にデータが送信されるたびに、ウィンドウ サイズは 2 ずつ指数関数的に拡張されます
。 4 8 16;
3. 初期しきい値に達すると、指数関数的に拡大することはなくなり、毎回 1 ずつ加算されて直線的に増加します;
4. ウィンドウが特定の値に達すると、大量のパケット損失が発生します。たとえば、頻繁にタイムアウト再送信が発生し、ネットワークが混雑していることを意味します。
5. 輻輳ウィンドウのサイズは直接最小値の 1 に戻り、輻輳ウィンドウの新しいしきい値も現在の輻輳の半分に調整されます。ウィンドウ;
6. 手順 1 ~ 5 を繰り返します。
データパケットが送信されるたびに、輻輳ウィンドウと受信側のバッファサイズが比較され、小さい方の値が実際の送信ウィンドウとして採用されます
ここに画像の説明を挿入

少量のパケット損失はタイムアウト再送のみをトリガーします。大量のパケット損失はネットワークが混雑していることを意味します。TCP 通信が開始されると、ネットワークのスループットは徐々に増加します。ネットワークが混雑すると、スループットはすぐに低下します。輻輳制御, 最終的には、TCP プロトコルは相手にできるだけ早くデータを送信したいと考えていますが、ネットワークに過大な圧力を引き起こす妥協を避ける必要もあります。
ここに画像の説明を挿入

応答の遅れ(効率化の仕組み)

送受信の過程で、受信機は常にデータを処理しており、受信機のバッファーの未使用部分は常に増加しています。応答を遅らせることで、バッファの最新の未使用サイズを送信者に返すことができるため、ウィンドウ サイズが増加し、ネットワークの送受信の効率が向上します

ここに画像の説明を挿入

1. インターバル応答: インターバルの数は通常 2 です。つまり、毎回応答するわけではなく、一度 2 つのリクエストを受信して​​ 1 回応答します。たとえば、2 4 6 8 の答えです。しかし、たった 3 回ですべての送信処理が終了するのであれば、遅延により ACK を返すことはできないはずです。
2. 制限時間: 最大遅延時間を超えた場合に 1 回応答します。システムにはデフォルト値 (通常は 200ms) があり、これは変更可能です。

便乗(効率化の仕組み)

通常、受信側が SYN リクエストを受信すると、システム カーネルは直ちに ACK で応答します。実際の応答はアプリケーションプログラムが行うものであり、ACKのタイミングとはある程度の時間差があります。遅延応答の存在により、SYN メッセージと ACK メッセージが同時に送信される状況が発生する可能性があり、その場合、システムは 2 つのメッセージを 1 つに結合します。このメカニズムは、わずかに応答性があると呼ばれます。

注: わずかに応答するメカニズムはありますが、100% の確率で起こるわけではなく、これはシステム カーネルによって処理されます。

ストリーム指向

ここに画像の説明を挿入

受信側は送信側が送信したデータを受信側のバッファに入れますが、受信側のバッファは BYTE 配列であるため、メッセージの境界を効果的に区別できません。この現象はスティッキー パケット問題と呼ばれます。

粘着性のあるパッケージの問題を解決します。

1. メッセージの終わりに特別な区切り文字を追加して、メッセージの終わりをマークします。これを使用する場合は、特殊文字に従ってバッファの内容をインターセプトするだけです。
2. メッセージ本文の長さを記述するために特別に使用されるフィールドを使用して、メッセージ本文の特定の長さを識別します。

ここに画像の説明を挿入

メッセージを読み取る前に、まずメッセージ本文の長さを示す 4 バイトのフィールドの内容を読み取り、その値は 42 です。
引き続きバッファ内の 42 バイトを読み取り、これらの 42 バイトがメッセージの内容を表します。
次に、4 バイトの内容を読み取ります。次のメッセージの長さ...を繰り返し実行するだけです。

JSON では中括弧を使用してメッセージをラップするため、メッセージの終わりを示す特殊文字として中括弧を使用していることがわかります。
アプリケーション層プロトコルである HTTP は、スティッキー パケットの問題を解決するために区切り文字が使用されている場合でも、メッセージの長さを示すフィールドを使用します。

TCP例外の処理

1.プログラムのクラッシュ: オペレーティング システムがそれを認識し、それに応じて処理します。オペレーティング システムは、ファイル記述子の解放を含むプロセスのリソースを再利用します。これは、対応するソケットのクローズを呼び出すのと同じであり、その後 FIN 操作をトリガーし、4 波に入り始めます。通常の四波動と何ら変わりません。
2.通常のシャットダウン: スタート メニューまたはシャットダウン コマンドの実行を通じて、システムはすべてのプロセスを強制的に終了し、リソースをリサイクルします。これはプログラム クラッシュ実行のプロセスと同様です。
3.ホストの電源障害: オペレーティング システムが応答しません。
受信機の電源がオフになっている: 送信者は受信機がハングアップしていることを知らず、データを送信し続けますが、データ送信後に ACK 応答を受信しないため、ACK 応答を受信せずに複数回の再送信でタイムアウト再送信がトリガーされ、接続をリセットしようとします (RST フラグ) 接続のリセットも失敗し、接続は放棄することしかできません。
送信側の電源障害: 通常、長時間の接続で発生し、サーバーとクライアントはハートビート パケットを維持します (サーバーがハートビートを受信して​​いない場合、クライアントは 1 秒ごとにサーバーにデータ パケットを送信し、サーバーが生きていることを証明します)オーバーなどのパケットを10秒以上受信しない場合は、クライアントがハングアップしていると判断し、お客様ご自身で接続を切断し、クライアントのネットワークが回復してから再接続してください。ホストは正常に動作してい
ます


続けてください~
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_43243800/article/details/131535424