Technologieaustausch

【JavaEE】Netzwerkprogrammierung – UDP

2024-07-12

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

Fügen Sie hier eine Bildbeschreibung ein
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

1. Datagramm-Socket (UDP)

1.1 Funktionen

  1. Verbindungslos: Es ist nicht erforderlich, vor der Datenübertragung einen eigenen Kanal einzurichten. Jedes Datenpaket wird unabhängig gesendet und wählt seinen eigenen Weg, um das Ziel zu erreichen. Genau wie beim Senden von Informationen müssen Sie nur die Informationen senden, die andere Partei nicht muss es erhalten.
  2. Unzuverlässige Übertragung: Bei der Übertragung von Daten müssen Sie nicht darauf achten, ob die andere Partei sie empfangen hat, sondern senden sie einfach.
  3. Am Datenfluss orientiert: Die Grundeinheit der Datenübertragung ist jedes UDP-Datagramm. Nach dem Lesen und Schreiben kann nur ein vollständiges UDP-Datagramm gelesen und geschrieben werden.
  4. Vollduplex: Zwei-Wege-Kommunikation ist über eine Verbindung möglich.

1.2 Kodierung

Die Socket-APIs werden alle vom System bereitgestellt. Die von verschiedenen Systemen bereitgestellten APIs sind unterschiedlich, aber diese System-APIs sind weiter in Java gekapselt.
Die Socket-API in UDP konzentriert sich auf zwei Klassen: DatagramSocket und DatagramPacket

1.2.1DatagrammSocket

Die Funktion dieser Klasse kann als Fernbedienung für die „Bedienung der Netzwerkkarte“ angesehen werden, also für Lese- und Schreibvorgänge auf der Netzwerkkarte, die als Lesen und Schreiben wie Dateien verstanden werden können.
Es stehen mehrere Methoden zur Verfügung:
Fügen Sie hier eine Bildbeschreibung ein

1.2.2DatagrammPaket

Diese Klasse beschreibt UDP-Datagramme. Ein DatagramPacket-Objekt entspricht einem UDP-Datagramm. Nach dem einmaligen Senden und Empfangen wird ein DatagramPacket-Objekt übertragen.

1.2.3 Implementieren Sie einen UDP-Echoserver (24*7).

Echo: Der Client sendet unterschiedliche Anfragen an den Server und der Server gibt unterschiedliche Antworten zurück. Aber das Echo hier ist das, was der Client an den Server anfordert, und der Server antwortet ohne Berechnung oder Geschäftslogik.

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

Fügen Sie hier eine Bildbeschreibung ein
Das DatagramPacket-Objekt stellt ein Datagramm dar. Ein UDP-Datagramm besteht aus einem Header und einer Nutzlast. Die IP-Adresse und die Portnummer im Header sind Attribute der DatagramPacket-Klasse, die Nutzlastklasse wird jedoch nicht bereitgestellt, sodass dies vom Programmierer durchgeführt werden muss Selbst bereitgestellt, also müssen Sie, genau wie ein Mönch, der um Almosen bittet, eine Schüssel für Almosen bereitstellen, und dann gibt jede Familie Essen in die Schüssel. Da das Wesentliche des Datagramms binäre Daten sind, stelle ich ein Byte-Array als Nutzlast bereit . Daten speichern.
Fügen Sie hier eine Bildbeschreibung ein
Die vom Client empfangenen Daten müssen nach der Verarbeitung durch den Server an den Client zurückgegeben werden. Daher muss ein DatagramPacket-Objekt erstellt werden, um die vom Server verarbeiteten Daten zu speichern, und der Server sendet sie dann an den Client. Zu diesem Zeitpunkt muss das DatagramPacket-Objekt kein Leerzeichen bereitstellen. Geben Sie einfach die verarbeiteten Daten in dieses Objekt ein, da beim Empfang der Daten nur die IP-Adresse und die Portnummer des Clients aufgezeichnet werden werden beim Senden an den Client aufgezeichnet. Sie müssen die IP-Adresse und Portnummer des Clients über requestPacket.getSocketAddress() abrufen.
Fügen Sie hier eine Bildbeschreibung ein
Da der Server fest installiert ist, können Sie die Portnummer direkt selbst festlegen. Die Portnummer wird beim Start des Servers erstellt.
Warum benötige ich eine Portnummer-IP-Adresse?
Um eine erfolgreiche Kommunikation zwischen den beiden Parteien zu erreichen, müssen diese vier Kernindikatoren vorhanden sein:
Quellport, Quell-IP, Zielport, Ziel-IP
Durch diese vier Kernindikatoren kann eine Netzwerkkommunikation zwischen den beiden Parteien erreicht werden. Hier gibt es keine IP-Adresse des Servers, da sich Client und Server auf demselben Host befinden. Wir können eine IP verwenden, um 172.0.0.1 darzustellen, auch bekannt als Loopback-IP.

1.2.4 Implementieren Sie einen UDP-Client

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

Fügen Sie hier eine Bildbeschreibung ein
Für die von der Konsole empfangene Zeichenfolge ruft das requestPacket-Objekt die Referenz und Länge des Zeichenfolgenobjekts sowie seine eigene IP und seinen eigenen Schutz ab und sendet sie dann über die Sendemethode des Sockets an den Server.
Fügen Sie hier eine Bildbeschreibung ein
Um eine Antwort vom Server zu erhalten, müssen Sie im Voraus ein ResponsePackt-Objekt und ein leeres Byte-Array erstellen, um die vom Server empfangene Antwort zu empfangen.
Unterscheiden Sie einige Methoden:
Bevor wir Methoden unterscheiden, müssen wir die Konzepte von Bytes, Zeichen und Codierungsformen verstehen
Bytes und Zeichen sind im Wesentlichen binär, also Daten wie 0101, da der Computer nur 0101 erkennt.
Byte: Byte ist die Grundeinheit für die Datenspeicherung im Computer. 1 Byte besteht aus 8 Bits. Jedes Bit kann 0 oder 1 sein, was bedeutet, dass ein Byte 256 verschiedene Werte darstellen kann.
Zeichen: Zeichen sind die Grundeinheit für die Darstellung von Textinformationen. Je nach Codierungsform ist die Anzahl der einem Zeichen entsprechenden Bytes unterschiedlich. Durch die Codierungsform können Zeichen in Bytes umgewandelt werden, und dann können Bytes zur Darstellung von Daten verwendet werden . Daten übertragen und speichern.
Kodierungsform:

  1. ASCII-Code: Wird zur Darstellung englischer Zeichen verwendet.Es verwendet 7 Bit zur Darstellung von 128 Zeichen (einschließlich Groß- und Kleinbuchstaben, Zahlen, Satzzeichen und einigen Steuerzeichen), unterstützt jedoch keine chinesischen Zeichen. Hier entspricht ein Zeichen einem Byte
  2. Unicode (UTF8): UTF-8 ist eine Codierungsform mit variabler Länge, die Zeichen aus allen Schriftsystemen auf der ganzen Welt enthält. Sie ist mit dem ASCII-Zeichensatz kompatibel und unterstützt chinesische Zeichen, einschließlich Zeichen aus allen Schriftsystemen auf der ganzen Welt Zeichen entspricht 1-4 Zeichen Festival.
  3. GBK: verwendet eine Codierung mit variabler Länge, die hauptsächlich für die Codierung chinesischer Zeichen verwendet wird. Ein Zeichen entspricht 1-2 Bytes

Die Methoden sind unterschiedlich:

  1. getBytes(): Ruft eine Referenz auf das Byte-Array ab. Das Objekt ist ein Zeichen. Hier werden die Zeichen in Bytes umgewandelt, um die Daten zu speichern.
  2. getBytes().length: Ermitteln Sie die Länge des Byte-Arrays. Das Objekt ist ein Zeichen
  3. getData(): Es wird ein Verweis auf eine aus Zeichen bestehende Zeichenfolge erhalten. Das Objekt besteht aus Bytes. Hier werden die Bytes in Zeichen umgewandelt, um die Daten zu verwenden.
  4. getLength(): Die Länge der erhaltenen Zeichenfolge, das Objekt besteht aus Bytes
  5. getAddress(): Ruft die IP-Adresse ab, das Objekt ist DatagramPacket
  6. getPort(): Ruft die Prot-Portnummer ab, das Objekt ist DatagramPacket
  7. getSocketAddress(): Ruft die IP-Adresse und die Prot-Portnummer ab, das Objekt ist DatagramPacket
  8. InetAddress.getByName(): Diese Methode kann den Hostnamen analysieren und die IP-Adresse des Hostnamens zurückgeben oder direkt das InetAddress-Objekt der angegebenen IP-Adresse zurückgeben. Das erhaltene InetAddress-Objekt wird an den Server übergeben, und der Server kann es auflösen die IP durch Abrufen dieser Adresse.

1.2.5 Implementieren Sie einen UDP-Wörterbuchübersetzungsserver

Hier können wir die Ideen der Vererbung und des Polymorphismus anwenden, um diesen Server zu implementieren, da der wesentliche Unterschied zwischen diesem Wörterbuchübersetzungsserver und dem Echoserver in der Verarbeitung von Antworten besteht, sodass wir den wiederholten Code erben und wiederverwenden und den erforderlichen Code erweitern können.

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

Um diese Wörterbuchübersetzung zu implementieren, benötigen Sie außerdem eine Karte in der Datenstruktur und verwenden die Schlüssel-Wert-Paare in der Karte zum Speichern von Daten.
Fügen Sie hier eine Bildbeschreibung ein
Dann müssen bestimmte Geschäftsvorgänge nur noch die Prozessmethode neu schreiben.
Fügen Sie hier eine Bildbeschreibung ein
Obwohl sich dies bei der Ausführung auf das udpEchoServer-Objekt der übergeordneten Klasse bezieht, ist die ausgeführte Prozessmethode aufgrund der polymorphen Funktion tatsächlich der Prozess der Unterklassenmethode, da die Unterklasse udpDictServer die übergeordnete Klasse udpEchoServer erbt.

2. Client- und Serverprozess

  1. Daten von der Konsole abrufen
  2. Der Client sendet Daten an den Server
  3. Der Server empfängt die vom Client gesendeten Daten
  4. Antwort berechnen
  5. Der Server sendet die berechnete Antwort an den Client
  6. Der Client erhält die vom Server gesendete Antwort
  7. Gibt die vom Server gesendete und vom Client empfangene Antwort an die Konsole aus