文章目录
前言
UDP协议程序相对于TCP协议,就是一个广播喇叭给全村人听和两个人说悄悄话的差别。因此UDP的数据传输效率比TCP高,可以同时分享给所有在场的人。缺点就是不能保证数据能完整地传送到接收方那里,这个就像喇叭一样了,谁知道对方听没听,UDP只管广播。UPD和TCP协议都可以看作数据通讯协议。
一、UDP程序设计是什么?
UDP程序设计是指使用用户数据报协议(UDP)进行网络通信的程序设计。
UDP 是一种无连接的协议,它提供了一种简单的、不可靠的数据传输服务。与传输控制协议(TCP)相比,UDP 不提供可靠的数据传输和流控制机制,但具有较低的延迟和较小的数据传输量。
UDP程序中,套接字负责接收或发送数据包,数据包负责保存数据内容,就像外卖小哥从一家店拿了外卖运送到各个门口一样,套接字是小哥,数据包是外卖。数据包套接字和多播套接字,两个套接字都是一回事,它们都是实现主机接收或发送数据包的类,只不过一个是父类一个是子类的关系。数
二、使用步骤
UDP协议的使用如下:
发送数据包
(1)使用MulticastSocket()创建数据包套接字对象(记得绑定接口)。
(2)将该数据包套接字使用joinGroup(InetAddress inet)方法加入指定的广播组地址(提前创建InetAddress对象,广播组地址范围:224.0.0.0~239.255.255.255)。
(3)使用DatagramPacket()的构造方法创建要发送的数据包对象。
(4)使用DatagramSocket()的send(DatagramPacket 数据包对象)方法发送数据包。
接收数据包
(1)使用MulticastSocket(int port)创建数据包套接字,绑定到指定端口。
(2)将该数据包套接字使用joinGroup(InetAddress inet)方法加入指定广播方的广播组地址。
(3)使用DatagramPacket(byte[] buf,int length)创建字节数组,接收数据包对象。
(4)使用DatagramSocket类的receive(DatagramPacket 数据包对象)方法接收数据包。
1.数据包套接字与多播套接字
(1)数据包套接字(DatagramSocket类)
java.net包的DatagramSocket类用于创建可以发送和接收数据包的套接字。
DatagramSocket类的构造方法:
new DatagramSocket();
//该构造方法创建DatagramSocket对象,创建数据包套接字。
new DatagramSocket(int port);
//该构造方法在创建数据包套接字对象的同时,并绑定了套接字的接口。
new DatagramSocket(int port,InetAddress address);
//该构造方法在创建数据包套接字对象的同时,并绑定了套接字的接口和指定的地址。
创建好DatagramSocket对象,绑定了接口,再使用joinGroup()方法(已经使用过构造方法绑定地址的不用使用此方法),就可以发送和接收数据包了。
方法 | 功能描述 | 返回值 |
send(DatagramPacket packet) | 发送指定的数据报到广播组 | void |
receive(DatagramPacket packet) | 从广播组中接收数据报并将其存储在指定的数据包中 | void |
close() | 关闭DatagramSocket对象,关闭套接字 | void |
setSoTimeout(int timeout) | 设置套接字的读取超时时间(以毫秒为单位) | void |
getLocalPort() | 返回此套接字绑定到的本地端口号 | int |
connect(InetAddress address, int port) | 将此套接字连接到指定的远程地址和端口号 | void |
disconnect() | 断开与远程地址的连接 | void |
getInetAddress() | 返回与此套接字连接的远程主机的IP地址 | InetAddress |
getPort() | 返回与此套接字连接的远程主机的端口号 | int |
isClosed() | 检查套接字是否已关闭 | boolean |
isConnected() | 检查套接字是否已连接到远程主机 | boolean |
DatagramSocket()类的receive()方法接收数据时,如果还没有可以接收的数据,正常情况下receive()方法将阻塞,一直等到网络上有数据传来,receive()方法接收到该数据并返回。如果receive()方法没有阻塞,肯定程序出了问题,多数情况下时因为使用了一个被其他程序占用的端口号。
(2)多播套接字(MulticastSocket类)
MulticastSocket extends DatagramSocket
MulticastSocket类是上面的DatagramSocket的子类,这意味着MulticastSocket类都能使用DatagramSocket类的常用方法了,由于该子类有更多的方法供使用,所以在实际编程中,用MuticastSocket创建套接字更多一些。
MulticastSocket类的构造方法:
new MulticastSocket();
//创建一个未绑定到任何本地端口的MulticastSocket对象。
new MulticastSocket(int port);
//创建一个绑定到指定本地端口的MulticastSocket对象。
什么情况下使用MulticastSocket类?
(1)多播通信:应用程序需要向多个客户端发送数据,使用MulticastSocket更为合适。
(2)简化的多播管理:MulticastSocket提供了方便的方法,如 joinGroup() 和 leaveGroup(),使得管理多播组变得简单。使用这些方法,你可以轻松地加入或离开多播组,而不需要手动处理组地址和端口。
(3)自动处理:使用 MulticastSocket 时,系统会自动处理多播特性,例如适当的网络路由和数据包的复制,这样你就不需要担心底层的多播实现细节。
(4)适用场景:在局域网内的实时数据传输(如视频流、在线游戏等),那么 MulticastSocket是更好的选择
方法 | 功能描述 | 返回值 |
joinGroup(InetAddress group) | 将套接字加入到指定的多播组,允许套接字开始接收或发送到该组的多播数据 | void |
joinGroup(SocketAddress mcastaddr) | 通过指定的 SocketAddress 来加入多播组 | void |
leaveGroup(InetAddress group) | 将套接字从指定的多播组中移除,停止接收或发送该组的多播数据 | void |
leaveGroup(SocketAddress mcastaddr) | 通过指定的 SocketAddress 来离开多播组 | void |
setTImeToLive(int tti) | 设置数据包的生存时间(TTL),用于控制多播数据包在网络中的跳数。TTL 的值通常在 1 到 255 之间 | void |
getTimeToLive() | 获取当前设置的生存时间(TTL)值 | int |
getLoopbackMode(boolean loopbackMode) | 设置是否允许将发送到多播组的数据包环回到本地的套接字 | void |
getLoopbackMode() | 获取当前的环回模式设置 | boolean |
以上MulticastSocket类的常用方法,并没有写上继承自父类DatagramSocket类的方法,但该子类仍可以使用。子类的这些特有的常用方法使得MulticastSocket能够有效地管理多播组的加入和离开,以及控制多播数据的传输特性。
2.数据报包
数据包(DatagramPackage类)
java.net包的DatagramPacket类用于创建数据包对象,UDP程序中运输数据的包裹。
DatagramPacket类的构造方法:
new DatagramPackage(byte[] buf,int length);
//该构造方法在创建数据包对象的同时,指定了数据包的数据内容和数据长度
new DatagramPackage(byte[] buf,int length, InetAddress address,int port);
//该构造方法在创建数据包对象的同时,指定了数据包的数据内容和数据长度,
//还有数据包发送的目标地址和端口
创建数据包对象时,一般使用第二个构造方法,直接绑定UDP地址和端口,省事。
方法 | 功能描述 | 返回值 |
getData() | 返回接收数据包的缓冲区 | byte[] |
getLength() | 返回接收数据包的数据长度 | int |
getAddress() | 返回发送数据包的目标地址 | InetAddress |
getPort() | 返回发送数据包的目标端口 | int |
setData(byte[] buf) | 设置发送数据包的缓冲区 | void |
setLength(int length) | 设置发送数据包的数据长度 | void |
setAddress(InetAddress address) | 设置接收数据包的目标地址 | void |
setPort(int port) | 设置接收数据包的目标端口 | void |
以上方法都是用于调整和设置数据包的发送地址、接口,数据包的大小,字节长度等数据。
3.实操展示
项目设计:实时广播当地的天气,接收方可以为许多当地人的手机,必须实时且同步。
广播组程序:使用MulticastSocket类创建多播套接字,构造方法中绑定接口和加入UDP地址;创建线程,使用Date类,格式化好后,打包到数据包对象中,套接字使用方法发送数据包到指定UDP地址处。
import java.io.IOException;
import java.lang.Thread;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Date;
import java.text.SimpleDateFormat;
public class Sender extends Thread{
int port = 9898; //端口
InetAddress group; //广播组地址对象
MulticastSocket socket; //多播数据包套接字
@SuppressWarnings("deprecation")
public Sender(){
//广播组地址范围:224.0.0.0~239.255.255.255
try{
group = InetAddress.getByName("224.255.1.1"); //指定广播组地址
socket = new MulticastSocket(port); //套接字实例化并绑定接口
socket.joinGroup(group); //套接字加入UDP地址,可以向该地址接收或发送消息
}catch(UnknownHostException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
public void run(){
while (true) {
DatagramPacket packet;
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("HH:mm:ss");
String message = "["+sf.format(date)+"] 天气预报:当前天气,多云。";
byte data[] =message.getBytes();
packet = new DatagramPacket(data, data.length, group,port);
//创建数据报,实例化,将上述数据存入数据包中,绑定了发送的UDP地址和接口
try {
socket.send(packet); //向UDP地址发送该数据包
System.out.println("Sending: " + message);
Thread.sleep(1000);
} catch (IOException e) {
e.printStackTrace();
} catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Sender sender = new Sender(); //构造方法解决了套接字的问题,简化了代码
sender.start(); //启动线程
}
}
客户端程序:接收类继承JFrame类,在构造方法中调用超类,创建窗口,实现界面交互(接收或中止接收广播消息),文本域中显示接收到的消息;实现Runnable接口,创建数据包对象,用套接字的方法和while循环不断接收指定UDP地址的广播消息;实现了ActionListener接口,为两个按钮添加监听事件和交互反馈,“接收消息”按钮被点击时,启动线程的start()方法,接收消息。
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
import java.lang.Runnable;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
@SuppressWarnings("deprecation")
public class Receiver extends JFrame implements Runnable,ActionListener{
JButton ince = new JButton("接收消息");
JButton stop = new JButton("停止接收");
JTextArea inceAr = new JTextArea(10,10);
JTextArea inced = new JTextArea(10, 10);
Thread thread;
boolean getMessage = true; //是否接收广播
int port = 9898;
InetAddress group;
MulticastSocket socket;
public Receiver(){
super("广播数据报"); //引用超类构造方法,设置窗体标题
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //WindowConstants表示多个窗口的关闭方式
inceAr.setForeground(Color.BLUE);
JPanel north = new JPanel();
north.add(ince); //将按钮添加到面板north上
north.add(stop);
add(north,BorderLayout.NORTH);
JPanel center = new JPanel();
center.setLayout(new GridLayout(1,2)); //设置面板布局
center.add(inceAr); //将文本域添加到面板上
final JScrollPane scrollPane = new JScrollPane();
center.add(scrollPane);
scrollPane.setViewportView(inced);
add(center, BorderLayout.CENTER); //设置面板布局
validate(); //重新验证容器中的组件,刷新组件
setBounds(100, 50, 640, 380);
setVisible(true);
ince.addActionListener(this);
stop.addActionListener(this);
try{
group = InetAddress.getByName("224.255.1.1"); //指定广播组地址
socket = new MulticastSocket(port); //实例化多播数据包套接字
socket.joinGroup(group);
} catch (IOException e){
e.printStackTrace();
}
thread = new Thread(this);
}
@Override
public void run(){
while (getMessage) {
byte data[] = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length, group,port);
try {
socket.receive(packet);
String message = new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的内容"+message);
inced.append(message+"\n");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==ince){
ince.setBackground(Color.green);
stop.setBackground(Color.red);
if(!thread.isAlive()){
thread = new Thread(this);
getMessage = true;
}
thread.start();
}
if(e.getSource()==stop){
ince.setBackground(Color.red);
stop.setBackground(Color.green);
getMessage = false;
}
}
public static void main(String[] args) {
new Receiver();
}
}
运行结果:
广播组程序:
如图所示,广播组程序向指定的UDP地址每隔一秒就发送一次天气播报。
客户端程序(当广播组程序启动后才能运行):
如图所示,可以有多个客户端,同时且同步的获取到广播组广播的信息,只要它们连接的是同一个接口和UDP地址。
总结
以上就是对UDP程序设计的简单介绍,本文简单介绍了如何搭建UDP程序,该程序在互联网通讯中的应用涉及面十分广泛。如果有疑问或指正的地方,欢迎读者在评论区中留言。