JavaネットワークプログラミングとNIO詳細1:JAVAにおけるオリジナルのsocket通信メカニズム


この文書は次のとおりです.https://github.com/jasonGeng88/blog
このシリーズの文章はGitHubの「Java面接ガイド」倉庫に整理されます.もっと素晴らしい内容は私の倉庫で見てください.
https://github.com/h2pl/Java-Tutorial
好きならStarを押してくださいね
記事は個人ブログに同期されます.
www.how2playlife.com
本文は微信の公衆番号【Java技術江湖】の《軽視できないJavaネットプログラミング》の中の1篇で、本文の一部の内容はネットに由来して、本文のテーマをはっきり話すために、多くの私が良いと思う技術ブログの内容を統合して、その中のいくつかの比較的に良いブログの文章を引用して、権利侵害があれば、作者に連絡してください.
このシリーズの博文はあなたにどのようにコンピュータのネットの基礎知識から着手することを教えて、一歩一歩Javaのネットの基礎を勉強して、socketからnio、bio、aioとnettyなどのネットのプログラミングの知識まで、しかも実戦を行って、ネットのプログラミングはすべてのJavaのバックエンドの技師が必ず学習して理解しなければならない知識点で、更に言えば、あなたはLinuxの中のネットのプログラミングの原理を掌握する必要がありますIOモデル、ネットワークプログラミングフレームワークnettyの進歩原理を含めてこそ、Javaネットワークプログラミング全体の知識体系をより完全に理解し、自分の知識フレームワークを形成することができる.
あなたの学習成果をよりよくまとめ、検証するために、このシリーズの文章では、一部の知識点に対応する面接問題や参考解答も提供します.
本シリーズの文章に何かアドバイスがあったり、何か疑問があったりしたら、公衆番号「Java技術江湖」に注目して作者に連絡して、本シリーズの博文の創作と改訂に参加することを歓迎します.
現在の環境
  • jdk == 1.8

  • インテリジェントポイント
  • socketの接続処理
  • IO入出力ストリームの処理
  • 要求データフォーマット処理
  • 要求モデル最適化
  • シーン
    今日は、JAVAのソケット通信についてお話しします.ここでは、baiduサイトへの通信が必要であると仮定して、最も簡単な要求応答モデルを例に挙げます.私たちはJAVA原生のsocketでどのように実現しますか.
    ソケット接続の確立
    まず、socket接続(コアコード)を確立する必要があります.
    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);


    ソケット入出力ストリームの処理
    SOcket接続の確立に成功すると,その入出力ストリームを得ることができ,通信の本質は入出力ストリームの処理である.入力ストリームを介して、ネットワーク接続からアップロードされたデータを読み出し、出力ストリームを介してローカルのデータをリモートに送信します.
    socket接続は、実際にはファイルストリームの処理と少し似ており、IO操作が行われています.
    入力、出力ストリームコードを取得するには、次のようにします.
    //    
    InputStream in = socket.getInputStream();
    //
    OutputStream out = socket.getOutputStream();

    IOストリームの処理については、一般的に対応するパッケージクラスでIOストリームを処理しますが、直接処理する場合は、  byte[]  操作を行うのは比較的煩雑です.パッケージクラスを採用すれば、stringintなどのタイプで直接処理することができ、IOバイトの操作を簡素化することができます.
    以下  BufferedReader  と  PrintWriter  入出力のパッケージクラスとして処理します.
    //    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));
    }


    データ要求と応答
    ソケット接続、IO入出力ストリームがあれば、リクエストデータを送信し、リクエストの応答結果を取得します.
    IOパッケージクラスのサポートがあるため、文字列形式で直接転送することができ、パッケージクラスがデータを対応するバイトストリームに置き換えることができます.
    私たちはbaiduサイトとHTTPアクセスを行っているので、出力フォーマットを追加的に定義する必要はありません.標準的なHTTP伝送フォーマットを採用すれば、要求応答を行うことができます(特定のRPCフレームワークでは、カスタム通信フォーマットがある場合があります).
    要求されたデータ内容は以下のように処理される.
    public class HttpUtil { 
      
    public static String compositeRequest(String host){
    
        return "GET / HTTP/1.1\r
    " + "Host: " + host + "\r
    " + "User-Agent: curl/7.43.0\r
    " + "Accept: */*\r
    \r
    "; }

    }
    データコードは のとおりです.
    //     
    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));    } }

    に,クライアントをインスタンス することによってsocket の を す.
    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();         }    }}

    この は は に つように えますが、 が きくなると、アプリケーションには くのスレッドが します.サーバでは、 スレッドが にファイルハンドルを めていることはよく られています.サーバ のハンドル は られており、 のスレッドにより、スレッド の り えの もかなり きくなります.だからこの は の きいシーンの で、きっと えられないに いない.
  • マルチスレッド+スレッドプール
  • スレッドが すぎてだめである 、スレッドの を すればいいのではないでしょうか. したスレッド のみを してsocket を い,マルチスレッドの を し,システムのリソース を した.
    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 は2 N+1に されます.
    この は、 も れているように えます.それはもっと いのではないでしょうか.1つのスレッドが のsocket を に し、 socketの データが されていない 、ブロックしないほうがいいのではないでしょうか.この を「IO 」と ぶ.JAVAのnioパッケージでは、 する が されています.
    1:TCPクライアントとサービス
    public class TCP    {
    public static void main(String[] args) {
    new Thread(new Runnable() {br/>@Override
    public void run() {
    try {
    Socket s = new Socket("127.0.0.1",1234); // IO
    InputStream is = s.getInputStream();
    OutputStream os = s.getOutputStream(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
    //
    bw.write(" ,
    ");
    bw.flush(); //
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String mess = br.readLine();
    System.out.println(" :"+mess);
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }).start();
    }
    }
    public class TCP    {
    public static void main(String[] args) {
    new Thread(new Runnable() {br/>@Override
    public void run() {
    try {
    ServerSocket ss = new ServerSocket(1234);
    while (true) {
    System.out.println(" ....");
    Socket s = ss.accept();
    System.out.println(" :" + s.getInetAddress().getLocalHost() + " ");
    BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    //
    String mess = br.readLine();
    System.out.println(" :" + mess);
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    bw.write(mess + "
    ");
    bw.flush();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }).start();
    }
    }

    2:UDPクライアントとサービス
    public class UDP    {
    public static void main(String[] args) {
    new Thread(new Runnable() {br/>@Override
    public void run() {
    byte []arr = "Hello Server".getBytes();
    try {
    InetAddress inetAddress = InetAddress.getLocalHost();
    DatagramSocket datagramSocket = new DatagramSocket();
    DatagramPacket datagramPacket = new DatagramPacket(arr, arr.length, inetAddress, 1234);
    datagramSocket.send(datagramPacket);
    System.out.println("send end");
    } catch (UnknownHostException e) {
    e.printStackTrace();
    } catch (SocketException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }).start();
    }
    }
    public class UDP    {
    public static void main(String[] args) {
    new Thread(new Runnable() {br/>@Override
    public void run() {
    try {
    DatagramSocket datagramSocket = new DatagramSocket(1234);
    byte[] buffer = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
    datagramSocket.receive(packet);
    System.out.println("server recv");
    String msg = new String(packet.getData(), "utf-8");
    System.out.println(msg);
    } catch (SocketException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }).start();
    }
    }

    に く
  • JAVAでIO を する
  • Nettyにおける の