ネットワークプログラミングによるソケット通信

このブログの内容が役立つ、またはインスピレーションを与えると思われる場合は、私のブログをフォローして、最新の技術記事やチュートリアルをできるだけ早く入手してください。同時に、コメント欄にメッセージを残して、ご意見やご提案を共有していただくこともできます。ご協力ありがとうございます!

1. ネットワークプログラミングとソケット通信

1.1 ネットワークプログラミングとは

ネットワーク プログラミングとは、コンピュータ ネットワーク間でデータを交換するためのアプリケーション プログラムを作成するプロセスを指します。ネットワークプログラミングは、分散システムの構築、クライアントとサーバー間のデータ通信の実現、ピアツーピア通信の実現などに役立ちます。

1.2 ソケット通信とは

ソケット通信は、ネットワーク プログラミングで最も一般的な通信方法であり、TCP/IP プロトコル スタックに基づいており、ネットワークを介して 2 台のコンピュータ間の接続を確立し、接続間でデータを送信します。ソケット通信は、異なるコンピュータ上で実行されている異なるプログラム間でデータを交換することができ、ネットワーク アプリケーションを構築する際の重要な部分です。

1.3 ソケット通信のメリット

ソケット通信には次のような利点があります。

  1. 信頼性: ソケット通信では TCP プロトコルを使用して、データ送信の信頼性を確保し、データの損失や破損が発生しないようにします。
  2. 柔軟性: ソケット通信は複数のネットワーク プロトコルと伝送方法をサポートしており、さまざまなアプリケーション シナリオで使用できます。
  3. 効率: ソケット通信は効率的なデータ伝送速度と低遅延を備えており、大量のデータ伝送のニーズを満たすことができます。
  4. 汎用性:ソケット通信はテキストデータだけでなく、マルチメディアデータやバイナリデータなどのさまざまなデータの送信にも使用できます。
  5. プログラマビリティ: ソケット通信はプログラミング インターフェイスであり、開発者は独自のニーズに従ってカスタム プログラミングを実行して、より複雑な機能を実現できます。

2、ソケット通信プロトコル

2.1 TCPプロトコル

TCP は Transmission Control Protocol の略称で、コネクション型で信頼性の高いプロトコルです。TCP プロトコルは、3 ウェイ ハンドシェイクを通じて接続を確立し、信頼性の高いデータ送信を保証します。TCP 接続では、データは複数のデータ パケットに分割され、各データ パケットにはシリアル番号が付けられて分類され、データ パケットを受信した後、受信側でシリアル番号に従って確認および再組み立てされ、正確性と完全性が保証されます。データの性別。

TCP プロトコルには次の特徴があります。

  1. 信頼性: TCP プロトコルは、確認および再送信メカニズムを通じて信頼性の高いデータ送信を保証します。
  2. 秩序ある: TCP プロトコルは、データを複数のデータ パケットに分割し、シーケンス番号に従って並べ替えて、データの秩序ある送信を保証します。
  3. 接続指向: TCP プロトコルは、通信前に接続を確立し、通信終了後に切断する必要があります。
  4. スロー スタートと輻輳制御: TCP プロトコルは、スロー スタートと輻輳制御メカニズムを通じてネットワークの輻輳とデータ損失を回避します。

TCP プロトコルは、Web ブラウジングやファイル転送など、信頼性の高い送信が必要なシナリオでよく使用されます。

2.2 UDPプロトコル

UDP は User Datagram Protocol の略で、コネクションレス型のプロトコルです。UDP プロトコルはデータをデータグラムにパッケージ化し、データの信頼性のある送信と順序を保証せず、確認や再送信は行わず、データグラムを宛先に送信するだけです。したがって、UDP プロトコルは遅延とネットワーク オーバーヘッドが低くなります。

UDP プロトコルには次の特徴があります。

  1. 接続なし: UDP プロトコルは接続を確立する必要がなく、データグラムを宛先に直接送信します。
  2. 信頼性の低さ: UDP プロトコルは、データの信頼性のある送信と順序を保証せず、確認と再送信を実行しません。
  3. 高速性: UDP プロトコルは遅延とネットワーク オーバーヘッドが低く、データを迅速に送信できます。

UDP プロトコルは、音声、ビデオ、ゲームなどのリアルタイム データ送信シナリオでよく使用されます。UDP プロトコルは遅延とネットワーク オーバーヘッドが低いため、リアルタイム要件を満たすことができます。

2.3 プロトコルの選択方法

TCP プロトコルと UDP プロトコルのどちらを選択するかは、アプリケーションの要件とシナリオによって異なります。

アプリケーションで信頼性の高いデータ送信と順序付けが必要な場合は、TCP プロトコルの方が適しています。たとえば、ファイル転送、Web ブラウジング、電子メールなどのアプリケーション シナリオでは、データの正確性と整合性を保証する必要があります。このとき、TCP プロトコルの確認および再送信メカニズムにより、データの信頼性のある送信と順序が保証されます。

アプリケーションが高速データ送信とリアルタイム パフォーマンスを必要とする場合は、UDP プロトコルの方が良い選択です。たとえば、リアルタイム音声、ビデオ、ゲームなどのアプリケーション シナリオでは、低遅延とネットワーク オーバーヘッドを満たす必要があります。現時点では、UDP プロトコルのコネクションレスでネットワーク オーバーヘッドが低いため、リアルタイム要件を満たすことができます。

要約すると、プロトコルの選択は、アプリケーションのニーズとシナリオに従って行う必要があります。アプリケーションが信頼性の高いデータ送信と秩序性を必要とする場合は、TCP プロトコルを選択する必要があります。アプリケーションが高速データ送信とリアルタイム パフォーマンスを必要とする場合は、UDP プロトコルを選択する必要があります。

3. Java でのソケットプログラミング

3.1 ソケットクラスとServerSocketクラス

Java でのソケット プログラミングでは、java.net パッケージの Socket クラスと ServerSocket クラスを使用します。

Socket クラスは、クライアントとサーバー間の接続を確立するために使用されます。次の 2 つのコンストラクターが提供されます。

Socket(String host, int port) throws UnknownHostException, IOException
Socket(InetAddress address, int port) throws IOException

このうち、第 1 の構築方法はサーバのホスト名とポート番号を指定する場合に使用され、第 2 の構築方法はサーバの IP アドレスとポート番号を指定する場合に使用されます。

ServerSocket クラスは、サーバー側でクライアントの接続要求を監視するために使用されます。コンストラクターと accept() メソッドが提供されます。

ServerSocket(int port) throws IOException
Socket accept() throws IOException

このうち、構築メソッドはサーバーのポート番号を指定するために使用され、accept() メソッドはクライアントからの接続要求を待つために使用され、クライアントが接続に成功すると、accept() メソッドは新しい値を返します。クライアントと通信するためのソケットオブジェクト。

3.2 クライアントとサーバー間の通信

クライアントとサーバー間の通信では、まず接続を確立してから、Socket オブジェクトの入力ストリームと出力ストリームを使用してデータを送信する必要があります。以下は、単純なクライアントとサーバー間の通信の例です。

サーバー側のコード:

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接...");

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        String line = in.readLine();
        System.out.println("客户端发送的消息:" + line);

        out.println("你好,客户端!");
        out.flush();

        socket.close();
        serverSocket.close();
    }
}

クライアントコード:

import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        out.println("你好,服务器!");
        out.flush();

        String line = in.readLine();
        System.out.println("服务器返回的消息:" + line);

        socket.close();
    }
}

この例では、サーバーはまず ServerSocket クラスを使用してサーバー ソケットを作成し、次に accept() メソッドを使用してクライアントの接続要求を待ちます。クライアントが正常に接続されると、accept() メソッドはクライアントと通信するための新しい Socket オブジェクトを返します。

サーバーは入力ストリームと出力ストリームを使用してクライアントと通信します。クライアントは Socket クラスを通じて接続を作成し、入力ストリームと出力ストリームを使用してサーバーとデータを送信します。

4. デモンストレーション例

Java でのソケット プログラミングは、TCP および UDP を使用した通信をサポートします。通信に TCP を使用すると、データの信頼性の高い送信と順序を保証できますが、送信のリアルタイム パフォーマンスに影響する可能性があります。通信に UDP を使用すると、送信のリアルタイム パフォーマンスは向上しますが、信頼性の高い送信と順序に影響する可能性があります。データ。

4.1 ソケット通信に TCP プロトコルを使用する

TCPを使用したソケット通信のサンプルコード:

サーバー側のコード:

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接...");

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        String line = in.readLine();
        System.out.println("客户端发送的消息:" + line);

        out.println("你好,客户端!");
        out.flush();

        socket.close();
        serverSocket.close();
    }
}

クライアントコード:

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);

        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream());

        out.println("你好,服务器!");
        out.flush();

        String line = in.readLine();
        System.out.println("服务器返回的消息:" + line);

        socket.close();
    }
}

4.2 ソケット通信に UDP プロトコルを使用する

UDPを使用したソケット通信のサンプルコード:

サーバー側のコード:

import java.net.*;

public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket serverSocket = new DatagramSocket(8888);
        System.out.println("服务器已启动,等待客户端连接...");

        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

        serverSocket.receive(receivePacket);
        System.out.println("客户端已连接...");

        String line = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("客户端发送的消息:" + line);

        InetAddress address = receivePacket.getAddress();
        int port = receivePacket.getPort();

        byte[] sendData = "你好,客户端!".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
        serverSocket.send(sendPacket);

        serverSocket.close();
    }
}

クライアントコード:

import java.net.*;

public class UDPClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket clientSocket = new DatagramSocket();

        InetAddress address = InetAddress.getByName("localhost");
        int port = 8888;

        byte[] sendData = "你好,服务器!".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
        clientSocket.send(sendPacket);

        byte[] receiveData = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

        clientSocket.receive(receivePacket);
        String line = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("服务器返回的消息:" + line);

        clientSocket.close();
    }
}

ソケット通信に UDP を使用する場合、データの送受信には DatagramSocket クラスと DatagramPacket クラスを使用する必要があります。サーバーはまず DatagramSocket オブジェクトを作成し、次に accept() メソッドを使用してクライアントからの接続要求を待ちます。クライアントが正常に接続されると、サーバーは DatagramPacket オブジェクトを使用して、クライアントから送信されたデータを受信します。

クライアントは、DatagramSocket クラスを使用して DatagramPacket オブジェクトを作成し、送信するデータを DatagramPacket オブジェクトに配置し、send() メソッドを使用してデータを送信します。データを受信した後、サーバーは DatagramPacket オブジェクトを使用してクライアントのアドレスとポートを取得し、DatagramPacket オブジェクトを使用してクライアントにデータを送信できます。

4.3 概要

Java では、Socket クラスと ServerSocket クラスを使用してソケット通信用の TCP プロトコルを実装したり、DatagramSocket クラスと DatagramPacket クラスを使用してソケット通信用の UDP プロトコルを実装したりできます。TCP プロトコルは、高いデータの信頼性と順序を必要とするシナリオに適しており、UDP プロトコルは、高いリアルタイム データを必要とするシナリオに適しています。

サンプル コード内の TCP 通信と UDP 通信は両方とも、単一のリクエストと応答としてモデル化されています。実際のアプリケーションでは、通常、マルチスレッドまたはスレッド プールを使用して処理できる複数のリクエストと応答を実装する必要があります。また、TCP コネクションの再接続、タイムアウト、切断処理、UDP データの消失や重複送信などの問題も考慮する必要があります。

5. いくつかの一般的なネットワーク プログラミングの問題とその解決策

5.1 ネットワーク遅延とタイムアウトの問題

ネットワーク遅延とタイムアウトの問題は、ネットワーク プログラミングで頻繁に発生する問題です。ネットワーク通信中に遅延やタイムアウトが発生すると、クライアントがサーバーからのレスポンスを受信できなかったり、サーバーがクライアントのリクエストを受信できなかったりすることがあります。この問題を解決するには、次の解決策を採用できます。

  • タイムアウト期間を設定する: Socket オブジェクトでタイムアウト期間を設定します。指定された時間内に応答またはリクエストが受信されなかった場合、SocketTimeoutException がスローされます。

Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置超时时间为5秒
  • ハートビート メカニズムを使用します。定期的にハートビート メッセージを相手に送信して、接続の有効性を確認します。一定時間相手からの応答がない場合は、接続が切断されたと判断できます。

5.2 ネットワークの混雑問題

ネットワークの輻輳とは、ネットワーク内で送信されるデータの量がネットワークの容量を超え、その結果、ネットワークにボトルネックが発生することを意味します。ネットワークの輻輳の問題により、データ送信の遅延、損失、重複が発生する可能性があります。この問題を解決するには、次の解決策を採用できます。

  • データ送信量の削減:不要なデータ送信を最小限に抑え、ネットワーク混雑の発生を軽減します。
  • フロー制御を使用する: フロー制御テクノロジーを使用して、データの送信速度を制限し、ネットワークの輻輳を回避します。
  • データ圧縮を使用する: データを圧縮してデータ送信量を削減し、ネットワークの輻輳の発生を軽減します。

5.3 パケット損失と重複の問題

データ パケットの損失と重複の問題とは、データ送信中に一部のデータ パケットが損失したり、繰り返し送信されたりすることを指します。この問題により、データ転送が不正確で不完全になる可能性があります。この問題を解決するには、次の解決策を採用できます。

  • 信頼できる伝送プロトコルを使用する: TCP プロトコルなどの信頼できる伝送プロトコルを使用して、データ伝送の信頼性を確保します。
  • シーケンス番号を使用する: パケットにシーケンス番号を付けて、失われたパケットや重複したパケットを識別します。
  • チェックサムを使用する: チェックサムを使用してデータ パケットをチェックし、データ送信の正確性を確認します。

5.4 セキュリティの問題

セキュリティ上の問題とは、ネットワーク送信中にハッカーによってデータが盗聴される可能性があり、データ漏洩やセキュリティ上の問題が発生する可能性があることを意味します。この問題を解決するには、次の解決策を採用できます。

  • 暗号化テクノロジーを使用する: データを暗号化してデータのセキュリティを確保します。
  • 本人認証技術の利用:本人認証を行い、不正な利用者によるアクセスを防止します。
  • ファイアウォール テクノロジーを使用する: ファイアウォール テクノロジーを使用してネットワークを保護し、ハッカーを防ぎます。

ネットワークプログラミングの問題とその解決策については、ここで概要を説明しただけなので、後ほど説明する記事を書く予定です。

6、ネットワークプログラミングのベストプラクティス

6.1 ブロッキング IO の使用を避ける

IO をブロックすると、IO 操作が完了するか例外が発生するまで、現在のスレッドの実行がブロックされます。同時実行性が高いシナリオでは、IO をブロックすると多数のスレッドがブロックされ、プログラムのパフォーマンスとスループットに影響します。したがって、ブロッキング IO の使用は避け、非ブロッキング IO または非同期 IO の使用を選択できます。

ノンブロッキング IO の実装では、ソケット チャネルをノンブロッキング モードに設定し、セレクターを使用してソケット チャネルをポーリングします。ソケット チャネルの読み取りまたは書き込みの準備ができると、対応する IO 操作が実行されます。ノンブロッキング IO の例を次に示します。

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
///

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
while (true) {
    int readyChannels = selector.select();
    if (readyChannels == 0) continue;
    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
    while (keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        if (key.isReadable()) {
            // 处理读操作
        } else if (key.isWritable()) {
            // 处理写操作
        }
        keyIterator.remove();
    }
}

6.2 スレッド プールを使用したスレッドの管理

同時実行性の高いシナリオでは、多数のスレッドを作成すると、システム リソースの無駄とスレッド切り替えのオーバーヘッドが発生し、プログラムのパフォーマンスとスループットに影響を与えます。したがって、スレッド プールを使用してスレッドを管理し、スレッドの作成と破棄のコストを削減し、スレッドの再利用率を高めることができます。

Java は、スレッド プール フレームワーク Executor および ExecutorService を提供し、スレッド プールを簡単に作成および管理できます。次に、スレッド プールを使用する例を示します。

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//

ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket socket = serverSocket.accept();
    executorService.submit(() -> handleRequest(socket));
}

6.3 NIO (New IO) API の使用

NIO (New IO) は、Java で提供される一連のノンブロッキング IO API であり、従来の IO API と比較して、高同時実行性および高スループットのシナリオを扱う際のパフォーマンスとスケーラビリティが優れています。NIO API の中核はチャネルとバッファであり、チャネルはデータの読み取りと書き込みを担当し、バッファはデータの保存を担当します。

import java.net.InetSocketAddress;
      import java.nio.ByteBuffer;
      import java.nio.channels.SelectionKey;
      import java.nio.channels.Selector;
      import java.nio.channels.ServerSocketChannel;
      import java.nio.channels.SocketChannel;
      import java.util.Iterator;
      import java.util.Set;

      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.socket().bind(new InetSocketAddress(8080));
      serverSocketChannel.configureBlocking(false);
      Selector selector = Selector.open();
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
      while (true) {
          int readyChannels = selector.select();
          if (readyChannels == 0) continue;
          Set<SelectionKey> selectedKeys = selector.selectedKeys();
          Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
          while (keyIterator.hasNext()) {
              SelectionKey key = keyIterator.next();
              if (key.isAcceptable()) {
                  ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                  SocketChannel socketChannel = serverChannel.accept();
                  socketChannel.configureBlocking(false);
                  socketChannel.register(selector, SelectionKey.OP_READ);
              } else if (key.isReadable()) {
                  SocketChannel socketChannel = (SocketChannel) key.channel();
                  ByteBuffer buffer = ByteBuffer.allocate(1024);
                  socketChannel.read(buffer);
                  buffer.flip();
                  byte[] bytes = new byte[buffer.remaining()];
                  buffer.get(bytes);
                  String message = new String(bytes);
                  // 处理消息
              }
              keyIterator.remove();
          }
      }

上記はネットワーク プログラミングのベスト プラクティスであり、ブロッキング IO を回避し、スレッド プールを使用してスレッドを管理し、NIO (New IO) API を使用することで、プログラムのパフォーマンスとスケーラビリティを向上させることができます。

セブン、Netty、MINAの枠組み

7.1 NettyとMINAとは何ですか

Netty と MINA は両方とも、Java 言語に基づいて開発されたネットワーク プログラミング フレームワークであり、高度な再利用性、拡張性、パフォーマンスを提供します。Netty は、保守可能で高性能なプロトコル サーバーとクライアントを迅速に開発できる NIO (New IO) クライアント/サーバー フレームワークです。MINA はイベント駆動型の非同期ネットワーク フレームワークであり、その目標は、ネットワーク アプリケーションに高性能で柔軟なサーバー/クライアント フレームワークを提供することです。

7.2 NettyとMINAの利点

Netty と MINA には次のような利点があります。

  • 高い再利用性: Netty と MINA はどちらも、使いやすく再利用性の高い一連の API を提供しており、開発者は効率的で保守が容易なネットワーク アプリケーションを迅速に構築できます。
  • スケーラビリティ: Netty と MINA は両方とも優れたスケーラビリティを備えており、実際のアプリケーションのニーズに応じてカスタマイズおよび開発できるため、より高いパフォーマンスとより優れた信頼性を実現できます。
  • ハイパフォーマンス: Netty と MINA はどちらも非同期のノンブロッキング I/O モデルを使用しており、これにより従来のブロッキング I/O モデルにおける I/O スレッドのブロッキング問題が回避され、それによってアプリケーションのパフォーマンスが向上します。
  • 複数のプロトコルのサポート: Netty と MINA はどちらも、TCP、UDP、HTTP などの複数のプロトコルをサポートしており、さまざまなアプリケーションのニーズを満たすことができます。
  • マルチプラットフォームのサポート: Netty と MINA は両方ともクロスプラットフォームであり、さまざまなオペレーティング システム上で実行できます。

7.3 NettyまたはMINAの選択方法

Netty または MINA を選択する場合は、特定のアプリケーションのニーズに応じて選択する必要があります。高性能プロトコル サーバーまたはクライアントを開発し、複数のプロトコルをサポートする必要がある場合は、Netty を選択できます。イベント駆動型の非同期ネットワーク アプリケーションを開発する必要がある場合は、MINA を選択できます。同時に、特定のアプリケーション シナリオとチームの開発経験を考慮して選択することもできます。どのフレームワークを選択する場合でも、最高のパフォーマンスと信頼性を実現するには、その特性と使用方法を深く理解し、実際のアプリケーションのシナリオと組み合わせて最適化およびデバッグする必要があります。

八、NettyとMINAの基本アーキテクチャ

8.1 Netty の基本アーキテクチャ

Netty のコアは、再利用性が高く、簡単に拡張できる非同期イベント処理メカニズムのセットであり、そのアーキテクチャには次のコンポーネントが含まれています。

  • チャネル: ネットワーク送信用のチャネル。データの読み取りと書き込み、接続ステータスとイベントの監視、イベント ハンドラのバインド/アンバインドが可能です。
  • EventLoop: すべてのイベントを処理し、それらを対応するイベント ハンドラーにディスパッチして処理するイベント ループ メカニズム。
  • ChannelPipeline: イベント プロセッサ チェーン。さまざまなイベント プロセッサを秩序ある方法で完全な処理チェーンに結合します。
  • ChannelHandler: 入出力イベントを処理し、特定のビジネス ロジックを実行するイベント ハンドラー。

8.2 MINAの基本アーキテクチャ

MINA の中核はイベント駆動型の非同期 I/O フレームワークであり、そのアーキテクチャには次のコンポーネントが含まれています。

  • IoSession: TCP/IP 接続を表し、基礎となるネットワーク I/O 操作と対応するステータス情報をカプセル化します。
  • IoFilterChain: 一連の IoFilter で構成されるイベント ハンドラー チェーン。各 IoFilter は、特定の種類のイベントの処理または IoSession のプロパティの変更を担当します。
  • IoFilter: イベント プロセッサ。入出力イベントの処理と特定のビジネス ロジックの実行を担当します。
  • IoProcessor: 読み取り、書き込み、受け入れ、接続などの I/O 操作の管理を担当します。
  • IoAcceptor と IoConnector: それぞれサーバー側とクライアント側で使用され、接続の受け入れと接続の作成を担当します。

9、デモンストレーション例

9.1 Netty を使用した HTTP サーバーの実装

Netty は軽量で高性能の HTTP プログラミング フレームワークを提供するため、Netty を使用して HTTP サーバーを実装すると、Web アプリケーションの開発と展開を大幅に簡素化できます。以下では、簡単なサンプル コードを通じて、Netty を使用して HTTP サーバーを実装する方法を示します。

pom.xml ファイルに Netty 依存関係を追加する必要があります。

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.65.Final</version>
        </dependency>

HTTPサーバーコード:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class HTTPServer {

    private final int port;

    public HTTPServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new HTTPServerInitializer());

            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("Server started and listening on " + future.channel().localAddress());

            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: " + HTTPServer.class.getSimpleName() + " <port>");
            return;
        }

        int port = Integer.parseInt(args[0]);

        new HTTPServer(port).start();
    }
}

HTTP サーバーの初期化コード:

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

public class HTTPServerInitializer extends ChannelInitializer<SocketChannel> {

    private static final HttpServerCodec CODEC = new HttpServerCodec();
    private static final HttpObjectAggregator AGGREGATOR = new HttpObjectAggregator(1024 * 1024);
    private static final ChunkedWriteHandler CHUNKED_WRITE_HANDLER = new ChunkedWriteHandler();
    private static final HTTPServerHandler SERVER_HANDLER = new HTTPServerHandler();

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(CODEC);
        pipeline.addLast(AGGREGATOR);
        pipeline.addLast(CHUNKED_WRITE_HANDLER);
        pipeline.addLast(SERVER_HANDLER);
    }
}

HTTP サーバー ハンドラー コード:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;

import java.io.File;
import java.nio.file.Files;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class HTTPServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        String uri = request.uri();

        if (uri.equals("/")) {
            uri = "/index.html";
        }

        String path = "." + uri;

        File file = new File(path);

        if (file.exists() && file.isFile()) {
            byte[] bytes = Files.readAllBytes(file.toPath());
            ByteBuf buffer = Unpooled.wrappedBuffer(bytes);
          
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buffer);
            response.headers().set(CONTENT_TYPE, "text/html");
            response.headers().set(CONTENT_LENGTH, buffer.readableBytes());

            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

上記のサンプル コードでは、HTTP リクエストと応答の処理を簡素化するために、Netty が提供する​HttpServerCodec​プロセッサ。では、受信したオブジェクトを処理し、要求された URI に従ってファイルのコンテンツを読み取るかどうかを判断し、ファイルのコンテンツを を通じてクライアントに返すことが。​HttpObjectAggregator​​ChunkedWriteHandler​​HTTPServerHandler​​FullHttpRequest​​ByteBuf​

上記のサンプル コードは単純な HTTP サーバーの実装にすぎず、学習とデモンストレーションにのみ適していることに注意してください。実稼働環境では、セキュリティ、パフォーマンス、スケーラビリティなどのさらなる問題を考慮し、それに応じて最適化および構成する必要があります。

一般に、Java は、開発者がさまざまなネットワーク アプリケーションを簡単に実装できるようにする豊富なネットワーク プログラミング API を提供します。ネットワーク アプリケーションを実装する場合、ネットワーク プロトコルの仕様に注意を払い、ネットワーク接続の異常状態に対処し、ネットワーク伝送のセキュリティと信頼性を確保する必要があります。

このブログの内容が役立つ、またはインスピレーションを与えると思われる場合は、私のブログをフォローして、最新の技術記事やチュートリアルをできるだけ早く入手してください。同時に、コメント欄にメッセージを残して、ご意見やご提案を共有していただくこともできます。ご協力ありがとうございます!

おすすめ

転載: blog.csdn.net/bairo007/article/details/132595896