Javaソースコードの分析とインタビューの質問-ソケットのソースコードとインタビューの質問

関連するブログ、ムークラス参照列この一連のJavaソースコードとシステムメーカーは簡潔Zhentiインタビュアー
この列の下には、GitHubのアドレスです:
ソースは解決:https://github.com/luanqiu/java8
記事デモ:HTTPS:// GitHubの。 com / luanqiu / java8_demo
クラスメートは必要に応じてそれを見ることができます)

導入言語
Socket 中国語翻訳はソケットと呼ばれます。4年または5年働いた多くの学生はこのAPIを使用していませんが、このAPIを使用している限り、重要なプロジェクトのコアコードである必要があります。

通常、誰もがダボ、gRPC、Spring CloudなどのさまざまなオープンソースのRPCフレームワークを使用しており、ネットワーク呼び出しを記述する必要はほとんどありません。次の3つのセクションは、本当に必要なときにこのコンテンツを補足するのに役立ちます。マニュアルの例として使用できます。

この記事と「ServerSocketソースコードとインタビューの質問」では、主にSocketとServerSocketのソースコードについて説明します。「実際の作業:スレッドプールの使用と組み合わせたソケット」の章では、実際の作業で2つのAPIがどのように実装されるかについて主に説明します。

1ソケット全体構造

Socketの構造は非常に単純です。Socketはシェルに似ています。Socketの初期化や接続の作成などのさまざまな操作をラップします。基盤となる実装はSocketImplによって実装されます。Socket自体のビジネスロジックは非常に単純です。

Socketの属性は多くなく、ソケットステータス、SocketImpl、読み取りと書き込みのステータスなどがあります。ソースコードは次のとおりです。
ここに画像の説明を挿入
ソケットステータスの変更は、新しいソケットなどの操作メソッドに対応しています(createImplメソッド) 、状態はcreated = trueに変わり、接続(接続)した後、状態はconnected = trueに変わります。

2初期化

Socketには多くのコンストラクタがあり、2つのカテゴリに分類できます。

  1. ノードのセットを作成するためのプロキシタイプ(Proxy)を指定します。DIRECT(直接接続)、HTTP(HTTPおよびFTP拡張プロトコルのプロキシ)、SOCKS(SOCKSプロキシ)の3つのタイプがあり、3つの異なるコードメソッドが異なるSocketImplに対応しています、それぞれ:PlainSocketImpl、HttpConnectSocketImpl、SocksSocketImpl、タイプに加えて、プロキシはアドレスとポートも指定します。
  2. デフォルトのSocksSocketImplが作成され、アドレスとポートをコンストラクタで渡す必要がありますソースコードは次のとおりです:
// address 代表IP地址,port 表示套接字的端口
// address 我们一般使用 InetSocketAddress,InetSocketAddress 有 ip+port、域名+port、InetAddress 等初始化方式
public Socket(InetAddress address, int port) throws IOException {
    this(address != null ? new InetSocketAddress(address, port) : null,
         (SocketAddress) null, true);
}

ここでのアドレスは、127.0.0.1やwww.wenhe.comなどのIPアドレスまたはドメイン名にすることができます。

このコンストラクターによって呼び出されるこの低レベルコンストラクターのソースコードを見てみましょう。

// stream 为 true 时,表示为stream socket 流套接字,使用 TCP 协议,比较稳定可靠,但占用资源多
// stream 为 false 时,表示为datagram socket 数据报套接字,使用 UDP 协议,不稳定,但占用资源少
private Socket(SocketAddress address, SocketAddress localAddr,
               boolean stream) throws IOException {
    setImpl();
 
    // backward compatibility
    if (address == null)
        throw new NullPointerException();
 
    try {
        // 创建 socket
        createImpl(stream);
        // 如果 ip 地址不为空,绑定地址
        if (localAddr != null)
            // create、bind、connect 也是 native 方法
            bind(localAddr);
        connect(address);
    } catch (IOException | IllegalArgumentException | SecurityException e) {
        try {
            close();
        } catch (IOException ce) {
            e.addSuppressed(ce);
        }
        throw e;
    }
}

それはソースコードから見ることができます:

  1. ソケットを構築するとき、TCPまたはUDPを選択できます。デフォルトはTCPです。
  2. ソケットの構築時にアドレスとポートが渡された場合、構築時にこのアドレスとポートにソケットを作成しようとします。
  3. Socketのパラメーターなしのコンストラクターは、SocksSocketImplのみを初期化し、現在のアドレスポートにバインドしません現在のアドレスとポートを使用するには、connectメソッドを手動で呼び出す必要があります。
  4. ソケットは、ネットワーク通信の言語レベルの抽象化として理解できます。基盤となるネットワークの作成、接続、およびクローズは、TCPまたはUDPネットワークプロトコル自体によって指定された標準のままです。ソケットはカプセル化の層を作成するためにJava言語のみを使用するため、より便利になります。使用。

3サーバーに接続する

connectメソッドは主にソケットクライアントをサーバーに接続するために使用されます。最下層がTCP層プロトコルの場合、クライアントとサーバー間の通信を準備するために3ウェイハンドシェイクを介してサーバーとの接続を確立します。最下のソースコードは次のとおりです。

public void connect(SocketAddress endpoint, int timeout) throws IOException {
}

connectメソッドには2つの入力パラメーターが必要です。最初の入力パラメーターは、サーバーのアドレスを表すSocketAddressです。次のような初期化には、InetSocketAddressを使用できます。newInetSocketAddress( "www.wenhe.com"、2000)。

2番目の入力パラメーターは、クライアントがサーバーに接続するための最大待機時間を示すタイムアウト時間(ミリ秒単位)の意味です。現在の待機時間を超えた場合、接続はまだ正常に確立されず、SocketTimeoutException例外がスローされます。0の場合、無限の待機を意味します。

4ソケットの一般的に使用される設定パラメーター

Socketの一般的な設定パラメーターはSocketOptionsクラスにあります。次に、それらを1つずつ分析してみましょう。以下の理解のほとんどは、クラスの注釈とネットワークから得られます。

4.1 setTcpNoDelay

このメソッドは、TCP_NODELAY属性を設定するために使用されます。属性のコメントは次のとおりです:この設定はTCPにのみ有効で、主にNagleアルゴリズムの使用を禁止し、trueは禁止、falseは使用、デフォルトはfalseを意味します。

Nagleアルゴリズムについては、ウィキペディアの説明を引用します。

Nagアルゴリズムは、送信されるパケットの数を減らすことによって[TCP / IP]ネットワークのパフォーマンスを向上させるもので、フォードエアロスペースにいたときにJohn Nagによって命名されました。
Nagのドキュメントでは、彼が「小さなパケットの問題」と呼んでいるものについて説明しています。アプリケーションは継続的に小さな単位のデータを送信し、一部はしばしば1バイトしか占有しません。TCPパケットには40バイトのヘッダー情報(TCPとIPv4はそれぞれ20バイトを占める)があるため、利用可能な情報が1バイトしかない41バイトのパケットになり、非常に無駄になります。この状況は、Telnetの作業段階でよく発生します。ほとんどのキーボード操作では1バイトのデータが生成され、すぐに送信されます。さらに悪いことに、低速のネットワーク接続では、これらのデータパケットが多数同時に送信され、輻輳や衝突が発生します。
Nagアルゴリズムは、一定量の出力データを合体させて1回送信することで機能します。特に、まだ確認されていない送信済みのデータパケットがある限り、送信者は送信前に一定量のデータが蓄積されるまでデータパケットをバッファリングし続けます。

アルゴリズムがオンおよびオフになっているシーンを要約します。

  1. Nagleアルゴリズムがオフになっている場合、マウスの移動やクリックなどの小さなデータパケットの場合、クライアントはすぐにサーバーと対話し、リアルタイムの応答は非常に高くなりますが、頻繁な通信は多くのネットワークリソースを消費します。
  2. Nagleアルゴリズムが有効になっている場合、アルゴリズムは自動的に小さなデータパケットをマージし、サーバーとやり取りする前に特定のサイズ(MSS)に達するまで待機します。利点は、通信回数が減ることです。欠点は、リアルタイムの応答が低下することです。

ソケットを作成すると、デフォルトでNagleアルゴリズムがオンになります。リアルタイムの要件に応じて、Nagleアルゴリズムをオフにするかどうかを選択できます。

4.2 setSoLinger

setSoLingerメソッドは、主にSO_LINGER属性値を設定するために使用されます。

コメントはおそらくこれを意味します:closeメソッドを呼び出すと、デフォルトは直接戻ることですが、値がSO_LINGERに割り当てられている場合、closeメソッドはブロックされます。SO_LINGER時間以内に、通信パーティがデータを送信するのを待ちます。時間が経過してもまだ完了していません最後に、TCP RSTが送信され、TCPを強制的に閉じます。

setSoLingerソースコードを見てみましょう:

// on 为 false,表示不启用延时关闭,true 的话表示启用延时关闭
// linger 为延时的时间,单位秒
public void setSoLinger(boolean on, int linger) throws SocketException {
    // 检查是否已经关闭
    if (isClosed())
        throw new SocketException("Socket is closed");
    // 不启用延时关闭
    if (!on) {
        getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on));
    // 启用延时关闭,如果 linger 为 0,那么会立即关闭
    // linger 最大为 65535 秒,约 18 小时
    } else {
        if (linger < 0) {
            throw new IllegalArgumentException("invalid value for SO_LINGER");
        }
        if (linger > 65535)
            linger = 65535;
        getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger));
    }
}

4.3 setOOBInline

setOOBInlineメソッドは、主にSO_OOBINLINE属性を設定するために使用されます。

メモには、TCP緊急データ(TCP緊急データ)を受け入れる場合は、このオプションをオンにすることができます。デフォルトでは、このオプションはオフになっています。ソケット#sendUrgentDataメソッドを使用して緊急データを送信できます。

大量の情報を照会した後は、この値をできるだけ設定しないようにし、TCP緊急データの使用を禁止することをお勧めします。

4.4 setSoTimeout

setSoTimeoutメソッドは、主にSO_TIMEOUT属性を設定するために使用されます。

メモには、ブロッキング操作のタイムアウト時間を設定するために使用されます。ブロッキング操作には、主に次のものが含まれます。

  1. ServerSocket.accept()サーバーはクライアントの接続を待ちます。
  2. SocketInputStream.read()クライアントまたはサーバーの読み取り入力タイムアウト。
  3. DatagramSocket.receive()。

ブロッキング操作の前にこのオプション設定する必要があります。タイムアウトになると、操作はまだブロックされており、InterruptedIOExceptionがスローされます(ソケットはSocketTimeoutException例外をスローします。ソケットが異なると、異なる例外がスローされる場合があります)。

ソケットの場合、タイムアウト時間が0に設定されていると、タイムアウト時間がなく、ブロック時に無期限に待機します。

4.5 setSendBufferSize

setSendBufferSizeメソッドは主にSO_SNDBUF属性を設定するために使用されます。入力パラメーターはint型であり、送信側(出力側)のバッファーのサイズはバイト単位で設定されます。

入力パラメーターのサイズは0より大きくなければなりません。そうでない場合、IllegalArgumentExceptionがスローされます。

通常、デフォルトを使用します。値が小さすぎると、ネットワークの相互作用が頻繁に発生する可能性があります。値が大きすぎると、相互作用が少なくなり、リアルタイムパフォーマンスが低下します。

4.6 setReceiveBufferSize

setReceiveBufferSizeメソッドは、主にSO_RCVBUF属性を設定するために使用されます。入力パラメーターはint型です。つまり、受信側のバッファーのサイズはバイト単位で設定されます。

入力パラメーターのサイズは0より大きくなければなりません。そうでない場合、IllegalArgumentExceptionがスローされます。

一般に、ソケットが確立された後は、ウィンドウサイズを自由に変更できますが、ウィンドウサイズが64kより大きい場合は、次の点に注意する必要があり
ます。1. ソケットがクライアントに接続する前にバッファ値を設定する必要があります
。2. ServerSocketにバインドする必要がありますローカルアドレスの前にバッファ値を設定します

4.7 setKeepAlive

setKeepAliveメソッドは、主にSO_KEEPALIVE属性を設定するために使用されます。主に、サーバー上のソケットがまだ生きているかどうかを検出するために使用されます。デフォルト設定はfalseで、この関数はトリガーされません。

SO_KEEPALIVEが有効になっている場合、TCPは自動的に関数をトリガーします。2時間以内にクライアントとサーバーソケット間の通信がない場合、TCPはキープアライブプローブを相手に自動的に送信し、相手はこのプローブに応答する必要があります(クライアントがサーバー)、3つの予測があります。

  1. サーバーは予想されるACK応答を使用して、すべてが正常であることを示します。
  2. サーバーはRSTで応答し、サーバーがクラッシュまたは再起動状態にあることを示し、接続を終了します。
  3. サーバーからの応答がありません(複数回試行されます)。これは、ソケットが閉じていることを示します。

4.8 setReuseAddress

setReuseAddressメソッドは主にSO_REUSEADDR属性を設定するために使用されます。入力パラメーターはブール値であり、デフォルトはfalseです。

ソケットが閉じられた後、実際に閉じられるまでしばらく待機します。この時点で新しいソケットが同じアドレスとポートをバインドするようになった場合、setReuseAddressがtrueであれば、バインドは成功します。そうでない場合、バインディングは失敗します。

5まとめ

常にビジネスコードを実行している場合、Socketはほとんど使用されない可能性がありますが、ネットワークプロトコルについてインタビューする場合、または将来ミドルウェアを実行する機会がある場合、Socketに接続する可能性が高いため、詳細をご覧ください。知識の予備としても良いです。

公開された40元の記事 ウォンの賞賛1 ビュー5354

おすすめ

転載: blog.csdn.net/aha_jasper/article/details/105609521