• 11.UDP-bite


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

    无连接:不需要建立连接,就可以交换数据
    不可靠:发送方不知道对方有没有收到数据
    面向数据报:以数据报为单位进行传输(一个数据报都会明确大小)一次发送/接收必须是一个完整的数据报,不能是半个,也不能是一个半
    全双工:一条链路双向通信
    半双工: 一条链路单向通信

    UDP版本的Socket

    两个核心类:
    1.DatagramSocket:创建一个UDP版本的Socket对象
    receive:接收数据
    send:发送数据
    close:释放资源

    2.DatagramPacket:
    表示一个UDP数据报,每次发送/接收数据,都是在传输一个DatagramPacket对象

    UDP版的实现一个回显服务

    1.服务器端:

    // 站在服务器的角度:
    // 1. 源 IP: 服务器程序本机的 IP
    // 2. 源端口: 服务器绑定的端口 (此处手动指定了 9090)
    // 3. 目的 IP: 包含在收到的数据报中. (客户端的IP)
    // 4. 目的端口: 包含在收到的数据报中. (客户端的端口)
    // 5. 协议类型: UDP
    public class UdpEchoServer {
        // 进行网络编程, 第一步就需要先准备好 socket 实例~ 这是进行网络编程的大前提.
        private DatagramSocket socket = null;
    	//构造服务器的Socket对象时需要显示的绑定一个端口号(port)
        public UdpEchoServer(int port) throws SocketException {
            socket = new DatagramSocket(port);
        }
    
        // 启动服务器.
        public void start() throws IOException {
            System.out.println("启动服务器!");
            // UDP 不需要建立连接, 直接接收从客户端来的数据即可
            while (true) {
                // 1. 读取客户端发来的请求,假设此处的UDP数据报最长是1024,实际的数据可能不够1024
                DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
                socket.receive(requestPacket); // 为了接受数据, 需要先准备好一个空的 DatagramPacket 对象, 由 receive 来进行填充数据
                // 把 DatagramPacket 解析成一个 String
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "UTF-8");
                // 2. 根据请求计算响应(由于咱们这是一个回显服务, 2 省略)
                String response = process(request);
                // 3. 把响应写回到客户端,这里要的是字节长度,response.getBytes().length是字节长度,response.length()是字符长度,在发送数据的时候,必须要指定,这个数据报发给谁?地址+电话.lP+port在
                // DatagramPacket构造方法中,指定了第三个参数(getSocketAddress)(用于服务器端发送),表示要把数据发给哪个地址+端口
    			// SocketAddress 就可以视为是一个类,里面包含了IP和端口
                DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                        requestPacket.getSocketAddress());
                socket.send(responsePacket);
                System.out.printf("[%s:%d] req: %s, resp: %s\n",
                        requestPacket.getAddress().toString(), requestPacket.getPort(), request, response);
            }
        }
    
        // 由于是回显服务, 响应就和请求一样了.
        // 实际上对于一个真实的服务器来说, 这个过程是最复杂的. 为了实现这个过程, 可能需要几万行, 几十万行代码....
        public String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            UdpEchoServer server = new UdpEchoServer(9090);
            server.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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    客户端:
    不指定端口号,意思是,让操作系统自己分配一个空闲的端口号
    通常写代码的时候,服务器都是手动指定的.客户端都是由系统自动指定的(系统随机分配一个)

    对于客户端来说,如果手动指定,也行,但是系统随机分配更好
    一个机器上的两个进程,不能绑定同一个端口
    客户端就是普通用户的电脑,天知道用户电脑上都装了什么程序,天知道用户的电脑上已经被占用了哪些端口.如果你手动指定一个端口,万一这个端口被别的程序占用,咱们的程序不就不能正常工作了嘛??
    而且由于客户端是主动发起请求的一方,客户端需要在发送请求之前,先知道服务器的地址+端口.

    但是反过来在请求发出去之前,服务器是不需要事先知道客户端的地址+端口~~

    public class UdpEchoClient {
        private DatagramSocket socket = null;
        private String serverIP;
        private int serverPort;
    
        // 站在客户端的角度:
        // 源 IP: 本机 IP
        // 源端口: 系统分配的端口
        // 目的 IP: 服务器的 IP
        // 目的端口: 服务器的端口
        // 协议类型: UDP
        public UdpEchoClient(String ip, int port) throws SocketException {
            // 此处的 port 是服务器的端口.
            // 客户端启动的时候, 不需要给 socket 指定端口. 客户端自己的端口是系统随机分配的
            socket = new DatagramSocket();
            serverIP = ip;
            serverPort = port;
        }
    
        public void start() throws IOException {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                // 1. 先从控制台读取用户输入的字符串
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 把这个用户输入的内容, 构造成一个 UDP 请求, 并发送.
                //    构造的请求里包含两部分信息:
                //    1) 数据的内容. request 字符串
                //    2) 数据要发给谁~ 服务器的 IP + 端口
                //    又使用到了一种 DatagramPacket 构造方法.既能构造数据,又能构造目标地址.这个目标地址是IP和端口分开的写法[用于客户端发送)
                DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                        InetAddress.getByName(serverIP), serverPort);
                socket.send(requestPacket);
                // 3. 从服务器读取响应数据, 并解析
                DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
                socket.receive(responsePacket);
                String response = new String(responsePacket.getData(), 0, responsePacket.getLength(), "UTF-8");
                // 4. 把响应结果显示到控制台上.
                System.out.printf("req: %s, resp: %s\n", request, response);
            }
        }
    
        public static void main(String[] args) throws IOException {
            // 由于服务器和客户端在同一个机器上, 使用的 IP 仍然是 127.0.0.1 . 如果是在不同的机器上, 当然就需要更改这里的 IP 了
            UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
            client.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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    传输层的UDP协议

    格式:
    在这里插入图片描述

    源端口:操作系统给客户端自动分配的端口
    目的端口:服务器的端口
    报文长度:包含UDP报文头长度和UDP数据长度
    校验和:是用来验证网络传输的这个数据是否是正确的(crc,md5,sha1…):

    此处报文长度是2个字节,范围0-65535,0-64k,这是UDP使用中一个非常致命的缺陷,无法表示一个比较大的数据报.

    下策:分包发送:但是太麻烦了,拆包组包的代码,写起来非常复杂,要考虑到很多情况(包丢了咋办,包的顺序错

    上策是直接改为TCP,

  • 相关阅读:
    猿创征文|走技术创新路,展时代宏图梦
    【附源码】计算机毕业设计java养老院老人日常生活管理系统设计与实现
    若依前端,菜单栏切换时刷新问题[页面菜单切换时,页面总是重新刷新,导致页面输入的查询参数重载清空]...
    .NET餐厅管理系统sql数据帮助类执行对单个Entity的更新(这个可以添加额外的约束条件)
    aj-report页面嵌入其他项目
    软件测试工程师面试题汇总
    在Linux上安装RStudio工具并实现本地远程访问【内网穿透】
    C++高级面试题:解释 C++ 中的行为参数化(Behavioral Parameterization)
    L1-056 猜数字
    算法入门(栈-01)
  • 原文地址:https://blog.csdn.net/m0_56182317/article/details/125427762