2021.10.21:UDP编程

与TCP编程相比,UDP编程就简单得多,因为UDP没有创建连接,数据包也是一次收发一个,所以没有的概念。

在Java中使用UDP编程,仍然需要Socket,因为应用程序在使用UDP时必须指定IP端口。注意:UDP和TCP虽然都使用0~65535,但是他们是两套独立的端口,即一个应用程序用TCP占用了端口1234,不影响另一个应用程序用UDP占用端口1234。

 

Server

Server端,使用UDP也需要监听指定端口。Java提供了DatagramSocket来实现这个功能,代码如下:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.charset.StandardCharsets;

public class Server {
    public static void main(String[]args) throws IOException {
        DatagramSocket ds = new DatagramSocket(6666);
        while(true){
            //数据缓冲区
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
            ds.receive(packet);//收到一个UDP数据包
            //收取到的数据存储在buffer中,由packet.getOffset(),packet,getLength()指定起始位置和长度
            //将其按照UTF-8编码转换为String
            String s = new String(packet.getData(),packet.getOffset(),packet.getLength(), StandardCharsets.UTF_8);
            //发送数据
            byte[] data="ACK".getBytes(StandardCharsets.UTF_8);
            packet.setData(data);
            ds.send(packet);
        }
    }
}

Server首先使用如下语句在指定的端口监听UDP数据包:

DatagramSocket ds = new DatagramSocket(6666);

如果没有其他应用程序占据这个端口,那么坚挺成功,我们就使用一个无限循环来处理收到的UDP数据包:

while(true){
    ...
}

要收到一个UDP数据包,需要准备一个byte[ ]缓冲区,并通过DatagramPacket实现接收

byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
ds.receive(packet);

假设我们收到的是一个String,那么,通过DatagramPacket返回的packet.getOffset()、packet.getLength()确定数据在缓冲区起止位置

String s = new String(packet.getData() , packet.getOffset() , packet.getLength() , StandardCharsets.UTF_8);

Server收到一个DatagramPacket后,通常必须回复一个或者多个UDP包,因为Client地址DatagramPacket中,每次收到的DatagramPacket可能是不同的Client,如果不回复,Client就收不到任何UDP包。

发送UDP包也是通过DatagramPacket实现的,发送代码非常简单:

byte[] data = ...
packet.setData(data);
ds.send(packet);

 

Client

与Server相比,Client使用UDP时,只需要直接向Server发送UDP包,然后接受返回的UDP包:

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

public class UDPClient {
    public static void main(String[]args) throws IOException {
        DatagramSocket ds = new DatagramSocket();
        ds.setSoTimeout(1000);
        //连接Server的IP与端口
        ds.connect(InetAddress.getByName("localhost"),6666);
        //发送
        byte[] data="Hello".getBytes();
        DatagramPacket packet = new DatagramPacket(data,data.length);
        ds.send(packet);
        //接收
        byte[] buffer = new byte[1024];
        packet = new DatagramPacket(buffer,buffer.length);
        ds.receive(packet);
        String resp = new String(packet.getData(),packet.getOffset(),packet.getLength());
        ds.disconnect();
    }
}

Client打开一个DatagramSocket使用如下代码:

DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"),6666);

Client创建DatagramSocket实例并不需要指定端口,而是由OS自动指定一个未使用的端口。紧接着,调用setSoTimeout(1000)设定超时1s,意思是后续接收UDP包时,等待时间最多不超过1s,否则在没有收到UDP包时,Client会无限等待下去。这不同于Server,因为Server可以无限等待。

注意到ClientDatagramSocket还调用了一个connect()方法"连接"到指定的Server。UDP是无连接协议,所以这里的connect()方法不是真连接,它是为了在ClientDatagramSocket实例中保存ServerIP端口,确保这个DatagramSocket只能往指定IP端口发送UDP包,不能往其他地址和端口发送。这么做不是UDP的限制,而是Java内置了安全检查。

如果Client希望向两个不同Server发送UDP包,那么它必须创建两个DatagramSocket实例。

后续的收发数据和Server端是一致的。通常来说,Client必须首先发送UDP包,以告知Server自己的IP和端口。

如果Client认为通信结束,就可以调用disconnect()断开连接:

ds.disconnect();

这里的disconnect()也不是真正断开连接,它只是清除了ClientDatagramSocket记录的ServerIP端口,这样,DatagramSocket就可以连接到另一个Server

 

小结

使用UDP协议通信时,Server和Client无需建立连接:

  • Server用DatagramSocket(port)监听端口;
  • Client用DatagramSocket.connect()指定远程IP端口
  • 双方通过receive()send()读写数据;
  • DatagramSocket没有IO流接口,数据被直接写入 byte[ ] 缓冲区。
posted @ 2021-10-21 21:58  ShineLe  阅读(45)  评论(0)    收藏  举报