• 网络编程 - UDP协议


    一,UDP基本概念

    UDP协议在传输层,有几个重要的特性:

    1. UDP是无连接的:UDP想要进行通信,不需要征得对方的同意,只要在send方法中指定目标的地址(UDP自身是不会存储对端的信息的)
    2. UDP是不可靠传输:UDP没有安全机制,它只负责发送,至于接收端有没有收到,没有收到后怎么处理,它都不关心。(UDP的传输效率更高)
    3. UDP是面向数据报的:这里的数据报是数据传输的一个单位。
    4. UDP是全双工的:UDP可以实现双向通信

     二,UDP协议端格式

    这两个图一个意思。为什么没有源IP和目的IP,因为IP协议在网络层不在传输层

    源端口号和目的端口号讲过了,下面我们重点聊一聊什么是UDP长度和UDP校验和

    2.1 UDP长度

    UDP长度是两个字节,16位表示的数据,表示范围 0 ~ 65535 => 64kb,即UDP数据报最大就是64kb.

    2.2 UDP校验和

    在网络传输过程中,可能会出现一些外部干扰,就会导致数据传输出错(传输的数据内容出错)。这时就需要有方法来识别出错数据,而UDP校验和就是这样的一种检查方法。

    校验和本质是一个字符串,是通过原始数据通过专门的算法生成的,但是体积比原始数据更小,如果原始数据相同,校验和一定相同,反之,校验和相同,原始数据大概率相同(理论上存在不同的可能性,但是概率极低,可忽略不计)

    校验的过程:

    1) 发送方把发送的数据整理好称为data1,通过专门的算法,计算出校验和sum1

    2) 发送方把data1和sum1一起通过网络发送给接收方

    3) 接收方把接受到的数据称为data2(由于干扰,可能数据出错),收到sum2(数据也可能出错)

    4) 接收方根据data2按照相同的算法,计算出校验和sum3

    5) 对比 sum2 和 sum3 是否相同,如果不同,则认为 data1 和 data2 一定不同。如果相同,则认为data1 和 data2 大概率相同(理论上存在不同的可能性,但是概率极低,工程上忽略不计)

    UDP协议中使用CRC算法(循环冗余算法)来计算校验和:把当前计算校验和的数据,每个字节都进行累加,把结果保存到UDP校验和中,如果数据溢出也没关系,如果中间某个数据传输错误,第二次计算的校验和与第一次不同。但是可能出现前一个字节恰好少1,后一个字节恰好多1这种类似的情况,虽然概率不大,但是还是会出现这种情况。

    介绍一种更加保险更加常见的算法:md5

    1)定长:md5的长度是固定的,无论你的数据有多长,计算出的md5都是固定的

    2)分散:给定两个原始数据,哪怕绝大部分内容相同,但只要有一个字节不同,得到的md5值的差异也会很大

    3)不可逆:给你一个md5值,要你还原出原始数据,由于计算量过于庞大,已经突破现有的算力极限,理论上是不可能的

    三,UDP中的Socket api

    Socket API 是系统提供的一组用于网络编程的应用程序接口,socket 本质上是一种特殊的文件,它是把网卡这个设备,给抽象成了文件,往socket文件中写数据,就相当于是通过网卡发送数据,从socket文件中读数据,就相当于通过网卡接收数据。

    在 JAVA 中使用 DatagramSocket 这个类来表示系统内部 socket 文件。使用 DatagramPacket 这个类来表示UDP数据报。

    3.1 DatagramSocket

    方法名说明
    DatagramSocket()创建一个Socket,绑定到本机任意一个没人使用的端口
    DatagramSocket(int port)创建一个Socket,绑定port端口
    void receive(DatagramPacket p)接收数据报p
    void send(DatagramPacket p)发送数据报p
    void close()关闭Socket

    3.2 DatagramPacket

    方法名说明
    DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)
    DatagramPacket(byte[] buf, int offset, int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号
    getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
    int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
    byte[] getData()获取数据报中的数据

    四,使用UDP实现客户端和服务器

    4.1 回显服务器

    1. import java.io.IOException;
    2. import java.net.DatagramPacket;
    3. import java.net.DatagramSocket;
    4. import java.net.SocketException;
    5. //回显服务器UDP
    6. public class Server {
    7. private DatagramSocket socket = null;
    8. public Server(int port) throws SocketException {
    9. socket = new DatagramSocket(port);//端口号
    10. }
    11. public void start() throws IOException {
    12. System.out.println("服务器启动");
    13. while(true){
    14. //1.读请求
    15. DatagramPacket request = new DatagramPacket(new byte[1024],1024);
    16. socket.receive(request);//如果没有请求,就阻塞
    17. String r = new String(request.getData(),0,request.getLength());//得到真实长度
    18. //2.根据请求计算响应
    19. String response = process(r);
    20. //3.返回响应
    21. DatagramPacket res = new DatagramPacket(response.getBytes(),
    22. response.getBytes().length,request.getSocketAddress());
    23. // response.length()得到字符长度,response.getBytes().length得到字节长度
    24. // 网络传输以字节为单位进行操作
    25. socket.send(res);
    26. //4.打印日志
    27. System.out.printf("[%s:%d] req=%s,res=%s\n",
    28. request.getAddress().toString(),request.getPort(),r,response);
    29. //为什么不close()?
    30. //socket是文件描述符表中的一个表象,因为我们不使用socket时,进程就要关闭,资源就全部释放了
    31. }
    32. }
    33. public String process(String request){
    34. return request;
    35. }
    36. public static void main(String[] args) throws IOException {
    37. Server server = new Server(9090);
    38. server.start();
    39. }
    40. }

    4.2 回显客户端

    1. import java.io.IOException;
    2. import java.net.*;
    3. import java.util.Scanner;
    4. public class Client {
    5. private DatagramSocket socket = null;
    6. private String serverIP = "";
    7. private int serverPort = 0;
    8. public Client(String ip, int port) throws SocketException {
    9. socket = new DatagramSocket();
    10. //UDP不会存储对端的信息,需要在应用程序中把对端的信息记录下来
    11. this.serverIP = ip;
    12. this.serverPort = port;
    13. }
    14. public void start() throws IOException {
    15. System.out.println("客户端请启动");
    16. Scanner scanner = new Scanner(System.in);
    17. while(true){
    18. //1.输入请求
    19. System.out.print("-> ");
    20. String req = scanner.next();
    21. //2.发给服务器
    22. DatagramPacket request = new DatagramPacket(req.getBytes(),req.getBytes().length,
    23. InetAddress.getByName(serverIP),serverPort);
    24. socket.send(request);
    25. //3.获取响应
    26. DatagramPacket res = new DatagramPacket(new byte[1024],1024);
    27. socket.receive(res);
    28. //4.显示响应
    29. String response = new String(res.getData(),0,res.getLength());
    30. System.out.println(response);
    31. }
    32. }
    33. public static void main(String[] args) throws IOException {
    34. Client client = new Client("10.162.153.3",9090);
    35. client.start();
    36. }
    37. }
  • 相关阅读:
    【干活分享-年薪百万以上】Java高端人才应具备的能力
    谱瑞PS186|替代PS186方案|TypeC转HDMI4K视频转换方案设计
    分享一个开发者和设计者的免费图标库
    网络安全学习心得分享~
    在JavaScript中实现用户输入一个个位数字,自动帮其补两个0,2位补一个,三位不补
    基于莱维飞行扰动策略的麻雀搜索算法(ISSA)(Matlab代码实现)
    机器学习之朴素贝叶斯
    信息学奥赛一本通(c++):1310:【例2.2】车厢重组
    吐槽嫌弃测试周期太长?开发自测一下
    栈和队列的几道OJ题(数据结构、C语言、LeetCode)
  • 原文地址:https://blog.csdn.net/m0_74859835/article/details/133654836