UDP协议程序设计

发布于:2024-09-18 ⋅ 阅读:(139) ⋅ 点赞:(0)

文章目录


前言

        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()方法(已经使用过构造方法绑定地址的不用使用此方法),就可以发送和接收数据包了。 

DatagramSocket类的常用方法
方法 功能描述 返回值
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是更好的选择

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地址和端口,省事。 

DatagramPacket类的常用方法
方法 功能描述 返回值
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程序,该程序在互联网通讯中的应用涉及面十分广泛。如果有疑问或指正的地方,欢迎读者在评论区中留言。


网站公告

今日签到

点亮在社区的每一天
去签到