ホームページ  >  記事  >  Java  >  Java がネイティブ ソケット通信メカニズムを実装する方法の原理の詳細な説明

Java がネイティブ ソケット通信メカニズムを実装する方法の原理の詳細な説明

黄舟
黄舟オリジナル
2017-08-20 09:10:401803ブラウズ

この記事では、JAVA でのネイティブ ソケット通信メカニズムの実装原理を主に紹介します。編集者が非常に優れていると考えたので、参考として共有します。編集者をフォローして見てみましょう。この記事では、JAVA のネイティブ ソケット通信メカニズムの原理を紹介し、それを皆さんと共有します。詳細は次のとおりです:

jdk == 1.8
。知識ポイント


ソケット接続処理

    IO入出力ストリーム処理
  • リクエストデータフォーマット処理
  • リクエストモデル最適化
  • シナリオ

今日は、お話しましょうJAVA でのソケット通信の問題。ここでは、Baidu サイトと通信する必要があると仮定して、最も単純な 1 リクエスト 1 レスポンス モデルを例として取り上げます。 JAVA のネイティブ ソケットを使用してこれを実装するにはどうすればよいでしょうか。

ソケット接続を確立します

まず、ソケット接続(コアコード)を確立する必要があります


import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
    
// 初始化 socket
Socket socket = new Socket();
// 初始化远程连接地址
SocketAddress remote = new InetSocketAddress(host, port);
// 建立连接
socket.connect(remote);

ソケットの入出力ストリームを処理します


ソケット接続が正常に確立された後、入力ストリームと出力ストリームを取得できるため、通信の本質は入力ストリームと出力ストリームの処理です。入力ストリームを通じてネットワーク接続からデータが読み取られ、出力ストリームを通じてローカル データがリモート エンドに送信されます。

ソケット接続は実際にはファイル ストリームの処理に似ており、どちらも IO 操作を実行します。
入力ストリームと出力ストリームを取得するコードは次のとおりです:

// 输入流
InputStream in = socket.getInputStream();
// 输出流
OutputStream out = socket.getOutputStream();

IOストリームの処理に関しては、通常、IOストリームを直接処理する場合は、対応するパッケージ化クラスを使用します。 []、これは比較的面倒です。ラッパークラスを使用すると、string や int などの型で直接処理できるため、IO バイト操作が簡素化されます。

以下では、処理のための入力および出力パッケージング クラスとして

を使用します。

// 获取 socket 输入流
private BufferedReader getReader(Socket socket) throws IOException {
  InputStream in = socket.getInputStream();
  return new BufferedReader(new InputStreamReader(in));
}

// 获取 socket 输出流
private PrintWriter getWriter(Socket socket) throws IOException {
  OutputStream out = socket.getOutputStream();
  return new PrintWriter(new OutputStreamWriter(out));
}

BufferedReader PrintWriter データのリクエストとレスポンス


ソケット接続とIO入出力ストリームを使用して、リクエストデータを送信し、リクエストの応答結果を取得します。

IO パッケージング クラスのサポートにより、文字列形式で直接送信でき、パッケージング クラスはデータを対応するバイト ストリームに変換するのに役立ちます。
HTTP 経由で Baidu サイトにアクセスしているため、追加の出力形式を定義する必要はありません。標準の HTTP 伝送形式を使用して、要求応答を実行できます (一部の特定の RPC フレームワークでは通信形式がカスタマイズされている場合があります)。

リクエストされたデータコンテンツは次のように処理されます:

public class HttpUtil {

  public static String compositeRequest(String host){

    return "GET / HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "User-Agent: curl/7.43.0\r\n" +
        "Accept: */*\r\n\r\n";
  }
  
}

リクエストデータを送信するコードは次のとおりです:


// 发起请求
PrintWriter writer = getWriter(socket);
writer.write(HttpUtil.compositeRequest(host));
writer.flush();
接收响应数据代码如下:

// 读取响应
String msg;
BufferedReader reader = getReader(socket);
while ((msg = reader.readLine()) != null){
  System.out.println(msg);
}

この時点で、接続の作成と送信のためのコアコードがすべて完了しました。ネイティブソケットでのリクエストとレスポンスの受信。

完全なコードは次のとおりです:


import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import com.test.network.util.HttpUtil;

public class SocketHttpClient {

  public void start(String host, int port) {

    // 初始化 socket
    Socket socket = new Socket();

    try {
      // 设置 socket 连接
      SocketAddress remote = new InetSocketAddress(host, port);
      socket.setSoTimeout(5000);
      socket.connect(remote);

      // 发起请求
      PrintWriter writer = getWriter(socket);
      System.out.println(HttpUtil.compositeRequest(host));
      writer.write(HttpUtil.compositeRequest(host));
      writer.flush();

      // 读取响应
      String msg;
      BufferedReader reader = getReader(socket);
      while ((msg = reader.readLine()) != null){
        System.out.println(msg);
      }

    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        socket.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

  }

  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream in = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(in));
  }

  private PrintWriter getWriter(Socket socket) throws IOException {
    OutputStream out = socket.getOutputStream();
    return new PrintWriter(new OutputStreamWriter(out));
  }

}

以下に、クライアントをインスタンス化してソケット通信を行った結果を示します。


public class Application {

  public static void main(String[] args) {

    new SocketHttpClient().start("www.baidu.com", 80);

  }
}

結果出力:


モデル最適化要求

このように、関数の実装には問題ありませんが。しかし、詳しく見てみると、IO の書き込みおよび読み取りプロセス中に IO ブロッキングが発生していることがわかります。つまり:


// 会发生 IO 阻塞
writer.write(HttpUtil.compositeRequest(host));
reader.readLine();

したがって、10 の異なるサイトを同時にリクエストしたい場合は、次のようになります:


public class SingleThreadApplication {

  public static void main(String[] args) {

    // HttpConstant.HOSTS 为 站点集合
    for (String host: HttpConstant.HOSTS) {

      new SocketHttpClient().start(host, HttpConstant.PORT);

    }

  }
}

最初のリクエストの応答が完了した後、次のサイトの処理が開始される必要があります。

これはサーバー側でより明らかですが、ここでのコードはクライアント接続ですが、具体的な操作はサーバー側のものと似ています。リクエストは 1 つずつ順番に処理することしかできず、これでは応答時間の基準を確実に満たせません。


マルチスレッド

    これはまったく問題ではないと考える人もいますが、JAVA はマルチスレッド プログラミング言語です。この状況には、マルチスレッド モデルが最適です。
  • public class MultiThreadApplication {
    
      public static void main(String[] args) {
    
        for (final String host: HttpConstant.HOSTS) {
    
          Thread t = new Thread(new Runnable() {
            public void run() {
              new SocketHttpClient().start(host, HttpConstant.PORT);
            }
          });
    
          t.start();
    
        }
      }
    }
この方法は、最初は便利に見えますが、大量の同時実行により、アプリケーションは大量のスレッドを使用することになります。ご存知のとおり、サーバー上では、各スレッドが実際にファイル ハンドルを占有します。サーバー上のハンドルの数は限られており、スレッドの数が多いと、スレッド間の切り替えにかなりの消費が発生します。したがって、この方法は、同時実行数が多いシナリオでは耐えられないはずです。


マルチスレッド+スレッドプール処理

    スレッドが多すぎるので、作成されるスレッドの数を制御するだけで済みます。ソケット処理では固定数のスレッドのみが開始されるため、マルチスレッド処理が利用されるだけでなく、システム リソースの消費も制御されます。
  • public class ThreadPoolApplication {
    
      public static void main(String[] args) {
    
        ExecutorService executorService = Executors.newFixedThreadPool(8);
    
        for (final String host: HttpConstant.HOSTS) {
    
          Thread t = new Thread(new Runnable() {
            public void run() {
              new SocketHttpClient().start(host, HttpConstant.PORT);
            }
          });
    
          executorService.submit(t);
          new SocketHttpClient().start(host, HttpConstant.PORT);
    
        }
    
      }
    }
起動スレッド数は、一般的にCPU集中型はN+1(NはCPUコア数)、IO集中型は2N+1となります。

この方法が最適だと思われます。スレッドが複数のソケット接続を同時に処理でき、各ソケットの入出力データの準備ができていないときにブロックされない場合、より良いものはありますか?この技術を「IO多重化」と呼びます。対応する実装は、JAVA の nio パッケージで提供されます。

以上がJava がネイティブ ソケット通信メカニズムを実装する方法の原理の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。