Partage de technologie

【JavaEE】Programmation réseau——UDP

2024-07-12

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

Insérer la description de l'image ici
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

1. Prise de datagramme (UDP)

1.1 Caractéristiques

  1. Sans connexion : il n'est pas nécessaire d'établir un canal dédié avant la transmission des données. Chaque paquet de données est envoyé indépendamment et choisit son propre chemin pour atteindre la destination. Tout comme pour l'envoi d'informations, il vous suffit d'envoyer les informations et l'autre partie ne le fait pas. il faut le recevoir.
  2. Transmission peu fiable : lors de la transmission de données, vous n'avez pas besoin de vérifier si l'autre partie les a reçues, envoyez-les simplement.
  3. Orienté vers le flux de données : l'unité de base de la transmission de données est chaque datagramme UDP. Une fois lu et écrit, un seul datagramme UDP complet peut être lu et écrit.
  4. Full-duplex : une communication bidirectionnelle est possible sur une seule liaison.

1.2 Encodage

Les API Socket sont toutes fournies par le système. Les API fournies par différents systèmes sont différentes, mais ces API système sont encapsulées en Java.
L'API socket dans UDP se concentre sur deux classes : DatagramSocket et DatagramPacket.

1.2.1DatagrammeSocket

La fonction de cette classe peut être considérée comme une télécommande pour « faire fonctionner la carte réseau », c'est-à-dire des opérations de lecture et d'écriture sur la carte réseau, qui peuvent être comprises comme une lecture et une écriture comme des fichiers.
Plusieurs méthodes sont proposées :
Insérer la description de l'image ici

1.2.2 Paquet de datagrammes

Cette classe décrit les datagrammes UDP. Un objet DatagramPacket est équivalent à un datagramme UDP. Une fois envoyé et reçu une fois, un objet DatagramPacket est transmis.

1.2.3 Implémenter un serveur d'écho UDP (24*7)

Echo : le client envoie différentes requêtes au serveur et le serveur renvoie différentes réponses. Mais l'écho ici est ce que le client demande au serveur, et le serveur répond sans aucun calcul ni logique métier.

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

Insérer la description de l'image ici
L'objet DatagramPacket représente un datagramme. Un datagramme UDP est composé d'un en-tête et d'une charge utile. L'adresse IP et le numéro de port dans l'en-tête sont des attributs de la classe DatagramPacket, mais la classe de charge utile n'est pas fournie, le programmeur doit donc le faire. Fourni, donc tout comme un moine demandant l'aumône, vous devez fournir un bol pour l'aumône, puis chaque famille met de la nourriture dans le bol. Puisque l'essence du datagramme est constituée de données binaires, je fournis un tableau d'octets comme charge utile. . Stocker des données.
Insérer la description de l'image ici
Les données reçues du client doivent être renvoyées au client après avoir été traitées par le serveur, donc un objet DatagramPacket doit être créé pour stocker les données traitées par le serveur, puis le serveur les envoie au client. Pour le moment, l'objet DatagramPacket n'a pas besoin de fournir un espace vide. Mettez simplement les données traitées dans cet objet puisque l'adresse IP et le numéro de port du client sont enregistrés lors de la réception des données, seuls l'adresse IP et le numéro de port du client. sont enregistrés lors de son envoi au client. Vous devez obtenir l'adresse IP et le numéro de port du client via requestPacket.getSocketAddress().
Insérer la description de l'image ici
Le serveur étant fixe, vous pouvez définir directement vous-même le numéro de port. Le numéro de port est créé au démarrage du serveur.
Pourquoi ai-je besoin d’une adresse IP de numéro de port ?
Pour parvenir à une communication réussie entre les deux parties, ces quatre indicateurs clés doivent être présents :
Port source, IP source, port de destination, IP de destination
Grâce à ces quatre indicateurs principaux, la communication réseau entre les deux parties peut être réalisée. Il n'y a pas d'adresse IP du serveur ici car le client et le serveur sont sur le même hôte. Nous pouvons utiliser une adresse IP pour représenter 172.0.0.1, également connue sous le nom d'adresse IP de bouclage.

1.2.4 Implémenter un client 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

Insérer la description de l'image ici
Pour la chaîne reçue de la console, l'objet requestPacket obtient la référence et la longueur de l'objet chaîne, ainsi que ses propres IP et Prot, puis l'envoie au serveur via la méthode d'envoi du socket.
Insérer la description de l'image ici
Pour recevoir une réponse du serveur, vous devez créer un objet ResponsePackt et créer au préalable un tableau d'octets vide pour recevoir la réponse reçue du serveur.
Il existe quelques méthodes :
Avant de distinguer les méthodes, nous devons comprendre les concepts d'octets, de caractères et de formes d'encodage.
Les octets et les caractères sont essentiellement binaires, ce qui correspond à des données telles que 0101, car l'ordinateur ne reconnaît que 0101.
Octet : L'octet est l'unité de base pour le stockage informatique des données. 1 octet correspond à 8 bits. Chaque bit peut être 0 ou 1, ce qui signifie qu'un octet peut représenter 256 valeurs différentes.
Caractère : le caractère est l'unité de base pour représenter les informations textuelles. Selon les différentes formes de codage, le nombre d'octets correspondant à un caractère sera différent. Grâce à la forme de codage, les caractères peuvent être convertis en octets, puis les octets peuvent être utilisés pour représenter les données. . Transférer des données et stocker des données.
Forme d'encodage :

  1. Code ASCII : utilisé pour représenter les caractères anglais.Il utilise 7 bits pour représenter 128 caractères (y compris les lettres majuscules et minuscules, les chiffres, les signes de ponctuation et certains caractères de contrôle), mais il ne prend pas en charge les caractères chinois. Ici, un caractère correspond à un octet.
  2. Unicode (UTF8) : UTF-8 est une forme de codage de longueur variable qui inclut les caractères de tous les systèmes d'écriture du monde. Il est compatible avec le jeu de caractères ASCII et prend en charge les caractères chinois, y compris les caractères de tous les systèmes d'écriture du monde. le caractère correspond à 1 à 4 caractères Festival.
  3. GBK : utilise un codage de longueur variable, principalement utilisé pour le codage de caractères chinois, un caractère correspond à 1-2 octets

Les méthodes sont différentes :

  1. getBytes() : Obtenez une référence au tableau d'octets. L'objet est un caractère Ici, les caractères sont convertis en octets pour stocker les données.
  2. getBytes().length : récupère la longueur du tableau d'octets, l'objet est un caractère
  3. getData() : Ce qui est obtenu est une référence à une chaîne composée de caractères. L'objet est ici des octets, les octets sont convertis en caractères pour utiliser les données.
  4. getLength() : La longueur de la chaîne obtenue, l'objet est en octets
  5. getAddress() : Récupère l'adresse IP, l'objet est DatagramPacket
  6. getPort() : récupère le numéro de port Prot, l'objet est DatagramPacket
  7. getSocketAddress() : récupère l'adresse IP et le numéro de port Prot, l'objet est DatagramPacket
  8. InetAddress.getByName() : Cette méthode peut analyser le nom d'hôte et renvoyer l'adresse IP du nom d'hôte, ou renvoyer directement l'objet InetAddress de l'adresse IP donnée. L'objet InetAddress obtenu est transmis au serveur, et le serveur peut résoudre. l'IP en obtenant cet objet.

1.2.5 Implémenter un serveur de traduction de dictionnaire UDP

Ici, nous pouvons appliquer les idées d'héritage et de polymorphisme pour implémenter ce serveur, car la différence essentielle entre ce serveur de traduction de dictionnaire et le serveur d'écho est le traitement des réponses, nous pouvons donc hériter et réutiliser le code répété et développer le code requis.

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

Pour implémenter cette traduction de dictionnaire, vous avez également besoin d'une carte dans la structure de données et utilisez les paires clé-valeur de la carte pour stocker les données.
Insérer la description de l'image ici
Il suffit ensuite de réécrire la méthode de processus pour des opérations commerciales spécifiques.
Insérer la description de l'image ici
Lors de l'exécution ici, bien que cela fasse référence à l'objet udpEchoServer de la classe parent, étant donné que la sous-classe udpDictServer hérite de la classe parent udpEchoServer et remplace la méthode de processus, la méthode de processus exécutée en raison de la fonctionnalité polymorphe est en fait le processus de la méthode de sous-classe.

2. Processus client et serveur

  1. Récupérer les données de la console
  2. Le client envoie des données au serveur
  3. Le serveur reçoit les données envoyées par le client
  4. Calculer la réponse
  5. Le serveur envoie la réponse calculée au client
  6. Le client reçoit la réponse envoyée par le serveur
  7. Imprime la réponse envoyée du serveur reçue du client vers la console