Java 无连接的数据报(UDP)

本文最后更新于:2024年3月18日 凌晨

Java 无连接的数据报(UDP)

  • 数据报是一种无连接的通信方式,它的速度比较快,但是由于不建立连接,不能保证所有数据都能送到目的地,所以一般用于传送非关键性的数据,发送和接收数据报需要使用Java类库中的DatagramPacket类和DatagramSocket类。

DatagramPacket类

  • DatagramPacket类是进行数据报通信的基本单位,它包含了需要传送的数据,数据报的长度,IP地址和端口等,DatagramPacket类的构造方法有如下两种:
    • DatagramPacket(byte [ ] buf, int n):构造一个用于接收数据报的DatagramPacket对象,buf是接收数据报的缓冲区,n是接收的字节数。
    • DatagramPacket(byte [ ] buf, int n, InetAddress address, int port):构造一个用于发送数据报的DatagramPacket对象,buf是发送数据的缓冲区,n是发送的字节数,address是接收机器的Internet地址,port是接收的端口号。
  • 也可以通过DatagramPacket类提供的方法获取或设置数据报的参数,如地址,端口等,例如通过一下命令设置和获取数据报的收发数据缓冲区。
    • void setData(byte [ ] buf):设置数据缓冲区。
    • byte [ ] getData():返回数据缓冲区。
  • 另外,DatagramPacket类还提供有如下常用方法。
    • int getLength():可用来返回发送或接收的数据报的长度。
    • InetAddress getAddress():返回数据报的主机地址。

DatagramSocket类

  • DatagramSocket类用来创建发送或接收数据报的DatagramSocket对象,它的构造方法有如下两种:
    • DatagramSocket():构造发送数据报的DatagramSocket对象。
    • DatagramSocket(int port):构造接收数据报的DatagramSocket对象,参数为端口号。

发送和接收过程

  • 要完成发送和接收数据报的过程,需要在接收端构造一个DatagramPacket对象指定接受的缓冲区,建立指定监听端口的DatagramSocket对象,并通过执行其receive()方法等待接收数据报。
  • 在发送端首先要构造DatagramPacket对象,指定要发送的数据,数据长度,接收主机地址及端口号,然后创建DatagramSocket对象,利用其send()方法发送数据报,接收端接收到后,将数据保存到缓冲区。

发送端程序

1
2
3
4
// 假定message为存放发送数据的字节数组。
DatagramPacket outpacket = new DatagramPacket(message,message.length,"192.168.0.3",80);
DatagramSocket outsocket = new DatagramSocket();
outsocket.send(outpacket);
  • 接收端的IP地址是192.168.0.3,端口号是80
  • 发送的数据在缓冲区message中,长度为200

接收端程序

1
2
3
4
5
byte[] buf = new byte[1024];//buf为接收缓冲区。
DatagramPacket inpacket = new DatagramPacket(buf,buf.length);
DatagramSocket insocket = new DatagramSocket(80);//80为接收端口号。
insocket.receive(inpacket);// 接收数据报。
String s = new String(buf,0,inpacket.getLength());// 将接收数据存入字符串。

[例16-3]:利用数据报发送信息或文本内容。

  • 以下程序利用数据报发送输入信息或文件内容到特定主机的特定端口。

发送程序

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
class UDPSend {
public static String usage = "用法:Java UDPSend <hostname> <port> <msg>...或 java UDPSend <hostname> <port> -f <file>";

public static void main(String[] args) {
try {
String host = args[0];
int port = Integer.parseInt(args[1]);
// 读取文件或字符串。
byte[] message;
if (args[2].equals("-f")) {
File f = new File(args[3]);
int len = (int) f.length();
message = new byte[len];
FileInputStream in = new FileInputStream(f);
in.read(message); // 从文件读全部数据存到数组中。
in.close();
} else {
String msg = args[2];
for (int i = 3; i < args.length; i++)
msg += " " + args[i];// 拼接信息内容。
message = msg.getBytes();// 字符串对应的字节数组。
}
// 创建连接。
InetAddress address = InetAddress.getByName(host);
DatagramPacket packet = new DatagramPacket(message, message.length, address, port);
DatagramSocket dsocket = new DatagramSocket();
// 发送数据。
dsocket.send(packet);
// 关闭连接。
dsocket.close();
} catch (Exception e) {
System.err.println(usage);
}
}
}
  • 说明:发送的数据来源有两种可能,通过命令行参数进行区分。
    • 一种是来自文件,要求第三个参数为-f,第四个参数为文件名,这种情形将从文件中读取信息写入到message字节数组。
    • 另一种从命令行直接输入文本,要求第三个参数不为-f,从第三个参数开始的所有输入信息均为数据。

接收程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class UDPReceive {
public static final String usage = "用法:java UDPReceive <port>";

public static void main(String[] args) {
try {
int port = Integer.parseInt(args[0]);
// 指定端口建立DatagramSocket对象。
DatagramSocket dsocket = new DatagramSocket(port);
byte[] buffer = new byte[2048];
// 建立数据报对象,并指定数据报的接收缓冲区。
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
for (; ; ) {
// 循环接收来自发送方的数据报。
dsocket.receive(packet);
// 将数据报接收缓冲区的数据转换为字符串。
String msg = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress() + ":" + msg);
}
} catch (Exception e) {
System.err.println(usage);
}
}
}
  • 注意:测试程序中,要首先运行接收程序,接收程序运行时需要提供一个端口号,该程序将循环等待接收来自发送方的数据报,发送程序需要的参数较多,首先要给定目标主机地址,端口号,接下来如果要发送文件内容,则-f后接文件名,否则剩下的所有参数均作为发送内容。

数据报多播

  • 所谓多播就是发送一个数据报文,所有组内成员均可以收到,多播通信使用D类IP地址,地址范围为224.0.0.1\~2239.255.255.255,发送广播的主机给指定多播地址的特定端口发送消息,接收广播的主机必须加入同一多播地址指定的多播组中,并从同样端口接收数据报,多播通信是一种高效率的通信机制,多媒体会议系统是其典型应用,MulticastSocketDataPacketSocket的子类,常用的构造方法如下:
    • MulticastSocket():创建一个多播Socket对象,可用于发送多播消息。
    • MulticastSocket(int port):创建一个与指定端口捆绑的多播Socket对象,可用于收发多播消息。
  • 多播消息通常带有一个严格的生存周期,它对应着要通过的路由器数量,默认消息的生存期数据为1,这种情形下。

接收多播数据

  • 接收方首先通过使用发送方数据报指定的端口号创建一个MulticastSocket对象。
  • 通过该对象调用joinGroup(InetAddress group)方法将自己登记到一个多播组中。
  • 然后就可用MulticastSocket对象的receive()方法接收数据报。
  • 在不需要接收数据时,可调用leaveGroup(InetAddress group)方法离开多播组。
  • 以下为接收多播数据报关键代码:
1
2
3
4
5
6
InetAddress group = InetAddress.getByName("228.5.6.7");// 多播组地址。
MulticastSocket s = new MulticastSocket(6789);// 创建MulticastSocket对象。
s.joinGroup(group);// 加入多播组。
byte[] buf = new byte[1000];
DatagramPacket recv = new DatagramPacket(buf,buf.length);
s.receive(recv);

发送多播数据

  • 发送方首先也要像接收方一样加入多播组。
  • 用MulticastSocket对象的send()方法发送数据报。
  • 以下为发送多播数据报关键代码:
1
2
3
4
5
6
InetAddress group = InetAddress.getByName("228.5.6.7");// 多播组地址。
MulticastSocket s = new MulticastSocket(6789);// 创建MulticastSocket对象。
s.joinGroup(group);// 加入多播组。
String msg = "Hello";
DatagramPacket hi = new DatagramPacket(msg.getBytes(),msg.length(),group,6789);
s.send(hi);
  • 其中,变量s为MulticastSocket对象,由于在发送的具体数据报中已指明了多播地址和端口,发送方创建MulticastSocket对象时也可使用不指定端口的构造方法。
  • 实际应用中,发送数据是主动的动作,而接受数据是被动的动作,为了不至于阻塞应用,对于接收可以创建一个专门的线程,让其循环等待接收数据。

[例16-4]:基于数据报多播技术的简单讨论区。

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
49
50
51
52
53
54
55
56
class Talk extends Frame implements Runnable {
MulticastSocket mSocket;// 用于收发数据的MulticastSocket对象;
TextArea display; // 显示消息的文本域。
TextField input; // 发送信息的文本框。
InetAddress inetAddress;// 多播地址。

public Talk() {
super("多播测试");
try {
mSocket = new MulticastSocket(7777);
inetAddress = InetAddress.getByName("230.0.0.1");
mSocket.joinGroup(inetAddress);
} catch (Exception e) {
}
//GUI
display = new TextArea(5, 40);
input = new TextField(20);
add("South", input);
add("Center", display);
setSize(200, 400);
setVisible(true);
//// 获取文本框的数据。
input.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e1) {
try {
byte[] data = input.getText().getBytes();
input.setText("");
// 通过数据报多播发送。
DatagramPacket packet = new DatagramPacket(data, data.length, inetAddress, 7777);
mSocket.send(packet);
} catch (Exception e) {
}
}
});
}

public static void main(String[] args) {
Talk s = new Talk();
new Thread(s).start();
}

public void run() {
try {
byte[] data = new byte[200];// 字节缓冲区存放接收数据。
DatagramPacket packet = new DatagramPacket(data, data.length);
// 循环接收来自用户的消息。
while (true) {
// 接收数据报。
mSocket.receive(packet);
// 将收到的数据添加添加到文本域中。
display.append(new String(data, 0, packet.getLength())+"\n");
}
} catch (Exception e) {
}
}
}
  • 说明:本程序在同一个类中实现数据的收发功能,类Talk在继承Frame窗体的同时实现Runnable接口,通过图形界面触发事件实现数据的发送。
  • 注意:多播通信已经通过一个组地址将用户联系在一起,因此,无须负责转发消息的服务方程序,只需运行一个程序就可以测试,但即使在单机上调试多播程序也必须注意保证网络连通,否则加入多播组将出现Socket异常。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!