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 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 ]; DatagramPacket inpacket = new DatagramPacket(buf,buf.length); DatagramSocket insocket = new DatagramSocket(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 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
,发送广播的主机给指定多播地址的特定端口发送消息,接收广播的主机必须加入同一多播地址指定的多播组中,并从同样端口接收数据报,多播通信是一种高效率的通信机制,多媒体会议系统是其典型应用,MulticastSocket
是DataPacketSocket
的子类,常用的构造方法如下:
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 ); 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 ); 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; 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) { } 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异常。