【网络编程通关之路】 Udp 基础回显服务器(Java实现)及你不知道知识原理详解 ! ! !

本篇会加入个人的所谓鱼式疯言

❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言

而是理解过并总结出来通俗易懂的大白话,

小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.

🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!

引言

在这个信息爆炸的时代,数据传输的速度和效率比以往任何时候都更加重要。想象一下,你正在观看一场在线直播的体育赛事,突然画面卡顿,数据包丢失------这可能是由于TCP协议的重传机制导致的延迟。

但如果你使用的是 UDP,情况可能会完全不同。UDP,即用户数据报协议,以其轻量级和低延迟的特性,在网络编程中扮演着重要角色。本文将深入探讨UDP的工作原理,以及它如何为现代网络通信带来革命性的变化。

目录

  1. Udp与Tcp 协议

  2. Udp服务器的实现过程及原理

  3. Udp客户端的实现过程及原理

  4. 回显服务器的原理剖析及扩展字典服务器功能

一. Udp 与 Tcp 协议

1. Udp 与 Tcp 的初识

Udp 和 Tcp 都是 Tcp / IP 五层协议中的 第四层: 传输层 , 主要提供在不同主机上提供进程和进程的 传输服务 , 主要还是关注传输的 起点和终点 。

2. Udp 的特点 和 Tcp的特点

Udp: 无连接, 不可靠, 面向 数据报, 全双工。

Tcp: 有连接, 可靠, 面向 字节流, 全双工

  • 连接性 :连接的含义: 不是实际意义上的两根线连起来,而是一种 抽象的连接 , 就是说通信双方都保存着对方的 信息(IP地址, 端口号)

    对于Udp 来说是无连接的,也就是 没有 保存 对方信息的一种连接特性。

    对于Tcp 来说是有连接的, 也就是 保存着 对方信息 的一种连接特性。

  • 可靠性 : 对于网络通讯来说可靠 不是百分百通信, 而不丢失数据 。 可靠性的关键就在于当 数据发送错误或出现数据丢失 的情况, 是否会进行 重传 数据。

    对于 Tcp 来说, 是有 应答响应和延时重传机制的(重点内容后面文章详解), 当网络数据发生影响时, 就会进行 重传防止数据的丢失

    对于 Udp 来说, 是没有的, 当网络通信出现问题时, 就会进行直接把这段数据直接丢弃, 不会发生重传

  • 面向数据报与面向字节流 : 这是两次不同协议的 传输单位 的不同

    对于Tcp 来说 , 面向字节流就等同于一个字节一个字节传输

    好比现有一堆数据, 总有二百个字节单位的数据

    如果你一个一个字节的传输, 需要传输两百次;

    如果你十个十个字节的传输, 需要传输二十次;

    如果你二十个字节的传输, 需要传输十次;

    如果你一百个字节的传输, 就需要传输两次;

    如果你二百个字节的传输, 就需要传输一次。

    对于Udp 来说, 是面向数据报的, 这里的数据报还比挖菜需要用锄头, 砍柴需要用斧头一样, 是一种 专门 为网络通信传输构造的一种 特殊结构的传输单位 。

    我们只需要构造好 数据报, 把需要传输的数据当 成一个整体 打包成 数据报 即可。

  • 全双工与半双工 : 双工本质上就是一种 传输的方向 , 无论是Tcp 和 Udp , 都是全双工, 其含义就是,无论对于这两种协议部署到主机上, 既可以 发送数据, 也可以 接受数据, 既可以 发送请求, 也可以 对请求做出响应 。 可以认为是一种双方向的
    而半双工就是部署好协议之后, 这个通信设备要么 只能接受数据 , 要么只能发送数据 , 而 不能同时接受数据和发送数据, 是以一种 单向 的形式传输。

二. Udp服务器的实现过程及原理

1. 引入

介绍完Tcp和Udp的特点之后,下面就开始上场本篇文章的核心内容,带着小伙伴们一起实践利用 Udp 协议写一个 回显服务器程序 。

什么是 回显 ???

就是这个服务器响应给客户端的是 客户端自身发来的请求 , 服务器这边不做业务/ 逻辑上 的处理, 而是直接把 原请求数据 响应返回给客户端
对于Udp 来说, 如果需要进行网络通信就需要调用 系统原生api 来进行实现 , 就需要了解 系统原生api 的使用细节, 但对于Java程序猿来说, JDK早已把 api 进行封装, 封装成 两个类: DatagramSocket , DatagramPacket 。 这两个类都来自于 Java.net 这个包中。

DatagramSocket 相关方法:

上述 对 DAtagramSocket 实例化一个 Socket 对象时, 我们就是利用这个 Socket 对象 来对 网卡进行操作 , 从而进行 网络通信的必要操作 。

DatagramPacket 相关方法:

DatagramPacket 则是上面谈及的数据报, 每实例化一个数据报时 , 就要传入对于的 byte[] 数组作为参数, 打包成 一个整体的数据报单元 进行传输。

想必小伙伴们应该还没有理解吧, 下面的原理部署和代码演示讲带着大伙熟悉这些方法的使用 💖 💖 💖 💖 💖

2. 服务器端代码演示

java 复制代码
package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;


public class MyServer {

    // 定义一个 数据包插口
   private   DatagramSocket socker  = null;


   // 构造方法
    public MyServer(int port) throws SocketException {
        // 进行进程和 端口号的连接
        socker = new DatagramSocket(port);

    }


    public void start()  throws IOException {

            while(true) {
                System.out.println("服务器开始运行...");

//        打包数据包基本单位
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096 );

//        接受客户端发来的数据
                socker.receive(requestPacket);

                // 转为成字符串打印日志
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength());


                // 执行业务逻辑并对客户端进行响应
                String response = process(request);


//              打包一组响应的数据包
//            注意这里要带上对应客户端输入的ip地址和端口号
                DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0, response.getBytes().length,
                        requestPacket.getSocketAddress());

                // 进行响应回服务器中
                socker.send(responsePacket);

                // 打印日志
                System.out.printf("[ip地址为:%s, %s,端口号为:%d, %d 客户端的的需求的为: %s, 服务器的响应为: %s]\n",
                        requestPacket.getAddress(),responsePacket.getAddress(), responsePacket.getPort(),requestPacket.getPort(), request, response);
            }
    }



    // 业务逻辑的方法
    public String process(String request) {
        return request;
    }



    public static void main(String[] args) throws IOException{
        MyServer  myServer = new MyServer(9090);
        myServer.start();
    }
}

3. 服务器端代码流程讲解

对于服务器这端来说:

整体 分为五部:

  1. 实例化 DatagramSocket 并以端口号作为 构造方法的参数 建立 端口号与进程 的连接。
java 复制代码
    // 定义一个 数据包插口
   private   DatagramSocket socker  = null;




   // 构造方法
    public MyServer(int port) throws SocketException {
        // 进行进程和 端口号的连接
        socker = new DatagramSocket(port);

    }
  1. 打包一个数据包用来 接收客户端 发来的 请求
java 复制代码
//        打包数据包基本单位
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096 );

//        接受客户端发来的数据
                socker.receive(requestPacket);
  1. 处理一定的业务逻辑, 由于我们这里是 回显服务器 ,只返回请求结果成为 服务器响应的内容 即可。
java 复制代码
     // 执行业务逻辑并对客户端进行响应
            String response = process(request);


// 业务逻辑的方法
public String process(String request) {
    return request;
}
  1. 构造一个 数据包 (带上效应的内容,长度, 以及地址) 用于把 服务器效应 发送回 服务器。
java 复制代码
//              打包一组响应的数据包
//            注意这里要带上对应客户端输入的ip地址和端口号
                DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0, response.getBytes().length,
                        requestPacket.getSocketAddress());

                // 进行响应回服务器中
                socker.send(responsePacket);
  1. 将 客户端的 IP地址, 客户端的 端口号请求效应 都 打印 出来。
java 复制代码
            // 打印日志
            System.out.printf("[ip地址为:%s, %s,端口号为:%d, %d 
            客户端的的需求的为: %s, 服务器的响应为: %s]\n",
                    requestPacket.getAddress(),responsePacket.getAddress(), 
responsePacket.getPort(),requestPacket.getPort(), request, response);
        }
}

鱼式疯言

  1. 由于 网络通信 需要的是 服务器和客户端 同时交互 才能展示效果,所以小伙伴们一定要记住只有两台机子以上才能完成 回显服务器 的实现。

  2. 端口号与进程 进行连接时, 需要注意的是:

    一个 进程能连接 多个 端口号

    但是 一个 端口号只能连接 一个 进程。

  3. 在获取 IP地址 和 端口号 打印时, 就需要通过Socket 的对象:responsePacket / requestPacket , 这两个数据报中不仅包含 数据内容 , 还含有 客户端 的 地址和端口号 等... 各种需要传递的信息。

  4. 最后还需要注意一点: Udp是 无连接 的特点, 所以当我们需要发送一个 请求/ 响应 时, 就需要在 对应的数据报 中加入 IP地址和端口号,用来指明需要发送到哪里?

三. Udp客户端的实现过程及原理

Udp 的实现 还是利用 DatagramSocketDatagramPacket 两个类来实现, 大体上的实现过程是 相似的 ,但有 细微之处不相同 。

下面我们来看看吧 💖 💖 💖 💖

1. 客户端的代码展示

java 复制代码
package network;


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;


public class MyClient {

    // 服务器ip
    private String serverIp ;

    // 服务器的端口号
    private int serverPort;

    // 创建一个客户端的插口
    private  DatagramSocket clientSocket =  null;



    public MyClient(String serverIp , int serverPort) throws SocketException {

        // 对该服务器的ip 和端口号进行赋值
        this.serverIp = serverIp;
        this.serverPort = serverPort;

        // 连接时,无须指定参数
        clientSocket = new DatagramSocket();

    }

    public void start() throws IOException{
       while(true) {
           System.out.println("客户端开始运行...");
           Scanner in= new Scanner(System.in);

           System.out.print("请输入你的需求: ");
//        输入
           String request = in.next();

           // 打包成 数据量基本单位
           // 并指定 ip 和 端口号
           DatagramPacket  requestPacket =  new 
           DatagramPacket(request.getBytes(), 
           request.getBytes().length, 
InetAddress.getByName(this.serverIp), this.serverPort);

           // 发送给服务器
           clientSocket.send(requestPacket);

           // 构造一个数据包基本单位接受服务器的响应
           DatagramPacket receivePacket = new DatagramPacket(
new byte[4096],4096);

           // 开始接收
           clientSocket.receive(receivePacket);

           System.out.println("最终服务器的效应为: ");

           // 输出日志
           String receive =new String(receivePacket.getData(),
0, receivePacket.getLength());

           System.out.println(receive);
       }

    }

    public static void main(String[] args) throws IOException {
        MyClient myClient = new MyClient("127.0.0.1",9090);
        myClient.start();
    }


}

2. 客户端的代码流程讲解

  1. 先实例化一个 Socket 对象 , 并得到 需要发送 服务器 的 IP地址和端口号 。
java 复制代码
public MyClient(String serverIp , int serverPort) throws SocketException {

    // 对该服务器的ip 和端口号进行赋值
    this.serverIp = serverIp;
    this.serverPort = serverPort;

    // 连接时,无须指定参数
    clientSocket = new DatagramSocket();

}
  1. 在控制台中 输入需求 ,并打包成 数据报 (带上数据内容, IP地址, 以及端口号) , 然后利用 Socket 对象进行发送。
java 复制代码
        System.out.println("客户端开始运行...");
           Scanner in= new Scanner(System.in);

           System.out.print("请输入你的需求: ");
//        输入
           String request = in.next();

           // 打包成 数据量基本单位
           // 并指定 ip 和 端口号
           DatagramPacket  requestPacket =  new 
           DatagramPacket(request.getBytes(), request.getBytes().length,
            InetAddress.getByName(this.serverIp), this.serverPort);

           // 发送给服务器
           clientSocket.send(requestPacket);
  1. 构造一个 数据报 用来接受服务器 返回的响应
java 复制代码
 // 构造一个数据包基本单位接受服务器的响应
           DatagramPacket receivePacket = new DatagramPacket(
new byte[4096],4096);

           // 开始接收
           clientSocket.receive(receivePacket);
  1. 打印 服务器响应 的日志
Java 复制代码
           // 输出日志
           String receive =new String(receivePacket.getData(),
0, receivePacket.getLength());

           System.out.println(receive);

鱼式疯言

补充细节

  1. 对于客户端这端来说, 当 实例化Socket 时, 是不需要指定 端口号 的, 因为系统就根据客户端的 空缺的端口号 自动默认分配 一个端口号
    如果 程序猿来 分配分配进程 就有可能造成 某个端口号正在忙碌, 就会出现 一个端口号绑定多个进程 的情况,就会参生 BUG
  2. 由于我们传入的IP 地址是 一个字符串 , 我们就需要转换为对于的 点分十进制 的地址表示方式: InetAddress.getByName(this.serverIp)
  3. 异常 SocketException 属于 IOException 的 子类

四. 回显服务器的原理剖析及扩展字典服务器功能

1. 回显服务器的原理剖析

具体 回显服务器 的流程小编用 图示 的方式表示啦,

  • 服务端等待客户端发送请求

  • 客户端发送以及阻塞等待

  • 返回客户端的响应

  • 整体的流程 :

鱼式疯言

  1. 从构造数据报的内容,我们可以看出 Udp面向数据报 的特点
  2. 从每次发送请求,或者返回响应都需要 设定IP地址和端口号 , 可以看出 Udp 无连接需要手动设置 IP 和端口号 的特点。
  3. 从客户端 既可以发送请求又可以接收响应 , 并且 服务器既可以接收请求,发送响应 , 充分说明 Udp是 全双工传输 的特点。

关于 Udp 不可靠传输 这个特点,这是一个难点也是重点, 小编会在后面的文章中重点详细的

2. 字典服务器功能

服务器:

java 复制代码
package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;





public class MyDicServer extends MyServer{

    private Map<String,String> map = new HashMap<>();

    public MyDicServer(int port) throws SocketException {
        super(port);
        map.put("cat","小猫");
        map.put("dog", "小狗");
        map.put("dusk", "小鸭");
    }

    @Override
    public String process(String request) {
        return  map.getOrDefault(request,"未知单词");
    }

    public static void main(String[] args) throws IOException {
        MyServer m = new MyDicServer(9090);
        m.start();
    }
}

客户端

java 复制代码
 package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;


public class MyServer {

    // 定义一个 数据包插口
   private   DatagramSocket socker  = null;


   // 构造方法
    public MyServer(int port) throws SocketException {
        // 进行进程和 端口号的连接
        socker = new DatagramSocket(port);

    }


    public void start()  throws IOException {

            while(true) {
                System.out.println("服务器开始运行...");

//        打包数据包基本单位
                DatagramPacket requestPacket = 
                new DatagramPacket(new byte[4096],
                        4096 );

//        接受客户端发来的数据
                socker.receive(requestPacket);

                // 转为成字符串打印日志
                String request = new String(requestPacket.getData(),
                        0, requestPacket.getLength());


                // 执行业务逻辑并对客户端进行响应
                String response = process(request);


//              打包一组响应的数据包
//            注意这里要带上对应客户端输入的ip地址和端口号
                DatagramPacket responsePacket = new
                 DatagramPacket(response.getBytes(),0,
                        response.getBytes().length,
                        requestPacket.getSocketAddress());

                // 进行响应回服务器中
                socker.send(responsePacket);

                // 打印日志
                System.out.printf("[ip地址为:%s, %s,端口号为:
                %d, %d 客户端的的需求的为: %s, 服务器的响应为: %s]\n",
                        
                        requestPacket.getAddress(),
                        responsePacket.getAddress(),
                        
                        responsePacket.getPort(),
                        requestPacket.getPort(),
                        
                        request, response);
            }
    }



    // 业务逻辑的方法
    public String process(String request) {
        return request;
    }



    public static void main(String[] args) throws IOException{
        MyServer  myServer = new MyServer(9090);
        myServer.start();
    }
}

字典服务器小编在这里只是作为扩展内容, 其实操作远不止这里, 至于字典服务器怎么生成的?

其实很简单, 只是在原来的基础上加入了一个 哈希表 进行返回, 即使不理解的小伙伴也无妨, 重要是理解前面Udp 的回显服务器的 实现原理 即可。

总结

  • Udp与Tcp 协议 : 主要说明的四种 UdpTcp 不同核心特点

  • Udp服务器的实现过程及原理: 先了解 实现回显服务器 Udp 的两个类, 分别是 操作网卡 进行网络通信的
    DatagramSocket 对象 与实现包装数据的 DatagramPacket 数据报。 并实现了

    服务器的代码以及流程讲解。

  • Udp客户端的实现过程及原理:实现了 Udp 的客户端代码 , 并且就客户端的特点进行了流程讲解。

  • 回显服务器的原理剖析及扩展字典服务器功能 : 从 图示的方式 进行理解回显服务器的原理, 并且扩展了 字典服务器

    的内容。

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正
希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

相关推荐
陌小路4 分钟前
5天 Vibe Coding 出一个在线音乐分享空间应用是什么体验
前端·aigc·vibecoding
敲上瘾6 分钟前
网络数据传输与NAT技术的工作原理
网络·智能路由器·信息与通信·nat
希赛网9 分钟前
《HCIA-Datacom 认证》希赛三色笔记:Vlan间三层通信过程解析
网络·智能路由器
java叶新东老师12 分钟前
Linux /proc/目录详解
linux·运维·服务器
谦行12 分钟前
前端视角 Java Web 入门手册 5.10:真实世界 Web 开发—— 单元测试
java·spring boot·后端
成长ing1213812 分钟前
cocos creator 3.x shader 流光
前端·cocos creator
Alo36520 分钟前
antd 组件部分API使用方法
前端
BillKu23 分钟前
Vue3数组去重方法总结
前端·javascript·vue.js
都给我34 分钟前
服务器中涉及节流(Throttle)的硬件组件及其应用注意事项
服务器·网络·express
ALe要立志成为web糕手37 分钟前
计算机网络基础
网络·安全·web安全·网络安全