技術共有

【JavaEE】ネットワークプログラミング—UDP

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

ここに画像の説明を挿入します
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

1. データグラムソケット (UDP)

1.1 特徴

  1. コネクションレス: データを送信する前に専用チャネルを確立する必要はありません。各データ パケットは独立して送信され、宛先に到達するための独自のパスを選択します。送信する必要があるのは情報だけであり、相手は必要ありません。それを受け取る必要があります。
  2. 信頼性の低い送信: データを送信する際、相手が受信したかどうかを気にする必要はなく、ただ送信するだけです。
  3. データ フロー重視: データ送信の基本単位は各 UDP データグラムです。一度読み書きできるのは、1 つの完全な UDP データグラムだけです。
  4. 全二重: 1 つのリンクで双方向通信が可能です。

1.2 エンコーディング

ソケット API はすべてシステムによって提供されます。システムごとに提供される API は異なりますが、これらのシステム API はさらに Java でカプセル化されます。
UDP のソケット API は、DatagramSocket と DatagramPacket の 2 つのクラスに焦点を当てています。

1.2.1データグラムソケット

このクラスの機能は、「ネットワークカードの操作」、つまりネットワークカードの読み書き操作を行うためのリモコンと考えることができ、ファイルの読み書きと同様に理解できます。
いくつかのメソッドが提供されています。
ここに画像の説明を挿入します

1.2.2データグラムパケット

このクラスは、UDP データグラムを記述します。DatagramPacket オブジェクトは、一度送受信されると送信されます。

1.2.3 UDP (24*7) エコー サーバーの実装

エコー: クライアントはさまざまなリクエストをサーバーに送信し、サーバーはさまざまな応答を返します。ただし、ここでのエコーはクライアントがサーバーに要求したものであり、サーバーは計算やビジネス ロジックなしで応答します。

public class udpEchoServer {
    public DatagramSocket socket = null;
    public udpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void  start() throws IOException {
        System.out.println("服务器启动!!!");
        while (true) {
            //接受客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //为了更好处理数据并方便打印,将接受的数据转化为字符串操作
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //处理客户端的请求(算术计算或者业务逻辑)
            String response = this.process(request);
            //将处理完的请求返还给客户端
            DatagramPacket resposePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(resposePacket);
            //打印客户端的ip地址端口号和接收客户端的数据以及处理完客户端后的数据
            System.out.printf("[%s:%d] req = %s,resp = %sn",requestPacket.getAddress(),requestPacket.getPort(),
                    request,response);
        }
    }
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        udpEchoServer udpEchoServer = new udpEchoServer(9999);
        udpEchoServer.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

ここに画像の説明を挿入します
DatagramPacket オブジェクトはデータグラムを表します。UDP データグラムはヘッダーとペイロードで構成されます。ヘッダー内の IP アドレスとポート番号は DatagramPacket クラスの属性ですが、ペイロード クラスは提供されないため、プログラマが行う必要があります。僧侶が托鉢するのと同じように、托鉢を用意し、各家族がその中に食べ物を入れます。データグラムの本質はバイナリデータであるため、ペイロードとしてバイト配列を提供しました。 . データの保存。
ここに画像の説明を挿入します
クライアントから受信したデータはサーバーで処理された後にクライアントに返される必要があるため、サーバーで処理されたデータを格納する DatagramPacket オブジェクトを作成し、サーバーがそれをクライアントに送信する必要があります。このとき、DatagramPacket オブジェクトには空白を設ける必要はなく、データ受信時にクライアントの IP アドレスとポート番号が記録されるため、このオブジェクトに処理済みのデータを入れます。クライアントに送信するときに記録されます。 requestPacket.getSocketAddress() を通じてクライアントの IP アドレスとポート番号を取得する必要があります。
ここに画像の説明を挿入します
サーバーは固定されているため、ポート番号はサーバーの起動時に作成されます。
ポート番号 IP アドレスが必要なのはなぜですか?
2 者間のコミュニケーションを成功させるには、次の 4 つの主要な指標が存在する必要があります。
送信元ポート、送信元IP、宛先ポート、宛先IP
これらの 4 つのコア インジケータを通じて、クライアントとサーバーが同じホスト上にあるため、ここではサーバーの IP アドレスはありません。ループバック IP とも呼ばれる 172.0.0.1 を表す IP を使用できます。

1.2.4 UDP クライアントの実装

public class udpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverProt;
    public  udpEchoClient(String serverIp, int serverProt) throws SocketException {
        socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverProt = serverProt;
    }
    public void start() throws IOException {
        System.out.println("客户端启动!");
        //1.从控制台获取字符串
        Scanner scanner = new Scanner(System.in);
        while(true) {
            System.out.println("请输入要发送的请求: ");
            String request = scanner.nextLine();
            //2.将输入的字符串发送给服务器,还要将发送的目标确定,IP和端口号
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0, request.getBytes().length,
                    InetAddress.getByName(serverIp), serverProt);
            socket.send(requestPacket);
            //3.从服务器读取到经过处理的响应
            DatagramPacket responsePackt = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePackt);
            //将响应打印在控制台
            String response = new String(responsePackt.getData(), 0, responsePackt.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        udpEchoClient udpEchoClient = new udpEchoClient("127.0.0.1",9999);
        udpEchoClient.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

ここに画像の説明を挿入します
requestPacket オブジェクトは、コンソールから受信した文字列について、文字列オブジェクトの参照と長さ、さらに独自の IP と Prot を取得し、ソケットの send メソッドを通じてサーバーに送信します。
ここに画像の説明を挿入します
サーバーからのレスポンスを受け取るには、responsePacktオブジェクトを作成し、サーバーからのレスポンスを受け取るための空のバイト配列を事前に作成しておく必要があります。
いくつかのメソッドを区別します。
メソッドを区別する前に、バイト、文字、エンコード形式の概念を理解する必要があります。
コンピュータは 0101 のみを認識するため、バイトと文字は基本的に 2 進数 (0101 のようなデータ) です。
バイト: バイトは、コンピュータがデータを保存するための基本単位です。1 バイトは 8 ビットであり、1 バイトが 256 個の異なる値を表すことができます。
文字: 文字は、テキスト情報を表す基本単位です。エンコード形式に応じて、文字に対応するバイト数が異なり、文字をバイトに変換してデータを表現できます。データとストアデータ。
エンコード形式:

  1. ASCII コード: 英語の文字を表すために使用されます。128 文字 (大文字と小文字、数字、句読点、一部の制御文字を含む) を表すのに 7 ビットを使用しますが、漢字はサポートされていません。ここで、1 文字は 1 バイトに相当します。
  2. Unicode (UTF8): UTF-8 は、世界中のすべての書記体系の文字を含む可変長エンコーディング形式であり、ASCII 文字セットと互換性があり、世界中のすべての書記体系の文字を含む漢字をサポートします。キャラクターは1~4のフェスティバルに対応します。
  3. GBK: 可変長エンコーディングを使用します。主に中国語の文字エンコーディングに使用され、1 文字は 1 ~ 2 バイトに対応します。

方法は異なります。

  1. getBytes(): バイト配列への参照を取得します。オブジェクトは文字です。ここでは、文字がバイトに変換されてデータが格納されます。
  2. getBytes().length: バイト配列の長さを取得します。オブジェクトは文字です。
  3. getData(): 取得するのは文字で構成される文字列への参照です。ここではバイトを文字に変換して使用します。
  4. getLength(): 取得した文字列の長さ、オブジェクトはバイトです
  5. getAddress(): IP アドレスを取得します。オブジェクトは DatagramPacket です
  6. getPort(): Prot ポート番号を取得します。オブジェクトは DatagramPacket です
  7. getSocketAddress(): IP アドレスと Prot ポート番号を取得します。オブジェクトは DatagramPacket です
  8. InetAddress.getByName(): このメソッドは、ホスト名を解析してホスト名の IP アドレスを返すか、指定された IP アドレスの InetAddress オブジェクトを直接返すことができ、取得された InetAddress オブジェクトはサーバーに渡され、サーバーが解決できます。このオブジェクトのアドレスを取得して IP を取得します。

1.2.5 UDP辞書翻訳サーバーの実装

ここで、継承とポリモーフィズムの考え方を適用してこのサーバーを実装できます。この辞書翻訳サーバーとエコー サーバーの本質的な違いは応答の処理であるため、繰り返しコードを継承して再利用し、必要なコードを拡張できます。

public class udpDictServer extends udpEchoServer{
    HashMap<String,String> map = null;
    public udpDictServer(int port) throws SocketException {
        super(port);
        map = new HashMap<>();
        map.put("server","服务");
        map.put("client","客户端");
        map.put("cat","🐱");
        map.put("dog","🐕");
        map.put("pig","🐖");
    }
    @Override
    public String process(String request) {
        return map.getOrDefault(request, "该词汇没有查询到");
    }

    public static void main(String[] args) throws IOException {
        udpDictServer udpDictServer = new udpDictServer(9999);
        udpDictServer.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

この辞書変換を実装するには、データ構造内にマップも必要であり、マップ内のキーと値のペアを使用してデータを保存します。
ここに画像の説明を挿入します
そうすれば、特定の業務操作は、プロセス メソッドを書き換えるだけで済みます。
ここに画像の説明を挿入します
ここで実行する場合、親クラスのudpEchoServerオブジェクトを参照していますが、udpDictServerサブクラスは親クラスのudpEchoServerを継承してprocessメソッドをオーバーライドしているため、ポリモーフィック機能により実行されるprocessメソッドは実際にはサブクラスメソッドのプロセスとなります。

2. クライアントとサーバーのプロセス

  1. コンソールからデータを取得する
  2. クライアントがサーバーにデータを送信する
  3. サーバーはクライアントから送信されたデータを受信します
  4. 応答の計算
  5. サーバーは計算された応答をクライアントに送信します
  6. クライアントはサーバーから送信された応答を受信します
  7. クライアントから受信したサーバーから送信された応答をコンソールに出力します。