网络编程 - UDP协议

一,UDP基本概念

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

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

 二,UDP协议端格式

网络编程 - UDP协议_第1张图片

这两个图一个意思。为什么没有源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 回显服务器

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

//回显服务器UDP
public class Server {
    private DatagramSocket socket = null;
    public Server(int port) throws SocketException {
        socket = new DatagramSocket(port);//端口号
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //1.读请求
            DatagramPacket request = new DatagramPacket(new byte[1024],1024);
            socket.receive(request);//如果没有请求,就阻塞
            String r = new String(request.getData(),0,request.getLength());//得到真实长度
            //2.根据请求计算响应
            String response = process(r);
            //3.返回响应
            DatagramPacket res = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,request.getSocketAddress());
            // response.length()得到字符长度,response.getBytes().length得到字节长度
            // 网络传输以字节为单位进行操作
            socket.send(res);
            //4.打印日志
            System.out.printf("[%s:%d] req=%s,res=%s\n",
                    request.getAddress().toString(),request.getPort(),r,response);
            //为什么不close()?
            //socket是文件描述符表中的一个表象,因为我们不使用socket时,进程就要关闭,资源就全部释放了
        }
    }
    public String process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        Server server = new Server(9090);
        server.start();
    }
}

4.2 回显客户端

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class Client {
    private DatagramSocket socket = null;
    private String serverIP = "";
    private int serverPort = 0;
    public Client(String ip, int port) throws SocketException {
        socket = new DatagramSocket();
        //UDP不会存储对端的信息,需要在应用程序中把对端的信息记录下来
        this.serverIP = ip;
        this.serverPort = port;
    }

    public void start() throws IOException {
        System.out.println("客户端请启动");
        Scanner scanner = new Scanner(System.in);
        while(true){
            //1.输入请求
            System.out.print("-> ");
            String req = scanner.next();
            //2.发给服务器
            DatagramPacket request = new DatagramPacket(req.getBytes(),req.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);
            socket.send(request);
            //3.获取响应
            DatagramPacket res = new DatagramPacket(new byte[1024],1024);
            socket.receive(res);
            //4.显示响应
            String response = new String(res.getData(),0,res.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        Client client = new Client("10.162.153.3",9090);
        client.start();
    }
}

你可能感兴趣的:(网络)