java基础-----网络通信--Udp通信

本文深入讲解Java中UDP通信的实现,包括DatagramSocket和DatagramPacket类的使用,提供了详细的案例代码,探讨了UDP丢包问题及解决方案,同时介绍了URL类的基本应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

UDP为用户数据报协议,在java中操纵UDP使用JDK中java.net包下的DatagramSocket和DatagramPacket类,可以方便的控制用户数据报文。DatagramPacket类将数据字节填充到UDP包中,这称为数据报。
DatagramSocket用来发送这个包。如果接受数据,可以从DatagramSocket中接受一个
DatagramPack对象,然后从该包中读取数据的内容。UDP是面向无连接的单工通信,它速度快

DatagramSocket类

构造函数:

DatagramSocket()  
创建实例,通常用于客户端编程,他并没有特定的监听端口,仅仅使用一个临时的。
DatagramSocket(int port) 
创建实例,并固定监听Port端口的报文。
DatagramSocket(int port, InetAddress laddr) 
这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。
DatagramSocket(SocketAddress bindaddr)	
bindaddr对象中指定了端口和地址。

常用函数:

receive(DatagramPacket p)	
接收数据报文到p中。receive方法是阻塞的,如果没有接收到数据报包的话就会阻塞在哪里。
send(DatagramPacket p)	
发送报文p到目的地。
setSoTimeout(int timeout)	
设置超时时间,单位为毫秒。
close()	
关闭DatagramSocket。在应用程序退出的时候,通常会主动的释放资源,关闭Socket,但是由于异常的退出可能造成资源无法回收。所以应该在程序完成的时候,主动使用此方法关闭Socket,或在捕获到异常后关闭Socket。

DatagramPacket类

DatagramPacket类用于处理报文,将字节数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成字节数组。

构造函数:

DatagramPacket(byte[] buf, int length, InetAddress addr, int port)	
从buf字节数组中取出offset开始的、length长的数据创建数据对象,目标地址是addr,目标端口是port。
DatagramPacket(byte buf[], int offset, int length, SocketAddress address)	
从buf字节数组中取出offset开始的、length长的数据创建数据对象,目标地址是address

常用函数:

getData() byte[]	
从实例中取得报文中的字节数组编码。
setData(byte[] buf, int offset, int length)
设置数据报包中的数据内容

案例

接收端

package TCP通信流程.UDP案例;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class TestReceive {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("等待发送端。。。。。发送数据");
        try {
            //指定接受端端口号,供发送端发送数据
            DatagramSocket datagramSocket = new DatagramSocket(9999);

            byte[] buf = new byte[1024];
            //指定数据包接收数据
            DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);
            //从数据包用getLength获取数据实际大小
            int length=datagramPacket.getLength();


            //接受数据
            datagramSocket.receive(datagramPacket);//将数据接收后用DatagramPacket数据包储存
            System.out.println("接收到发送端的数据");


            //打印输出将数据包中的字节数组转换成字符串,按照实际的大小去转换
            java.lang.String data=new java.lang.String(buf,0,length);
            System.out.println("发送端发出的信息:"+data);

            //回写给发送端
            //String data1 = "收到,谢谢";
            System.out.println("回写给发送端:");
            String data1=scanner.nextLine();

            //字符串数据转换
            byte[] bytes = data1.getBytes();
            //指定要连接那个接收端 指定IP 指定端口号 数据包类
            DatagramPacket datagramPacket1 = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 8888);

            //将准备好的数据包发送,到我们接受端,只需要调用一个方法就可以,(必须基于一个同一个通道)send方法
            datagramSocket.send(datagramPacket1);
            //关闭
            datagramSocket.close();

        } catch (Exception E) {
            System.out.println(E);

        }
    }
}

发送端

package TCP通信流程.UDP案例;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Testsend {
    public static void main(String[] args)  {
        Scanner scanner=new Scanner(System.in);

        try {
            //发送端和接受端进行通信的socket,
            DatagramSocket datagramSocket = new DatagramSocket(8888);//接受端回写数据包时所需端口号


            //一字符串形式存在
            //String data = "我爱学习";
            System.out.println("请输入要发送的内容:");
            String data=scanner.nextLine();
            //字符串数据转换
            byte[] bytes = data.getBytes();
            //指定要连接那个接收端 指定IP 指定端口号 数据包类
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 9999);

            //将准备好的数据包发送,到我们接受端,只需要调用一个方法就可以,(必须基于一个同一个通道)send方法
            datagramSocket.send(datagramPacket);


            //接收回写数据
            byte[] buf = new byte[1024];
            //指定数据包接收数据
            DatagramPacket datagramPacket1 = new DatagramPacket(buf, buf.length);
            //从数据包用getLength获取数据实际大小
            int length=datagramPacket1.getLength();
            //接受数据
            datagramSocket.receive(datagramPacket1);
            System.out.println("接收到接收端回写的数据");
            //打印输出将数据包中的字节数组转换成字符串,按照实际的大小去转换
            java.lang.String data1=new java.lang.String(buf,0,length);

            System.out.println("接收端回写的内容:"+data1);
            //关闭
            datagramSocket.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

UDP丢包案例

在传输过程中会发生数据不完整缺失,这就是我们所讲的丢包

接受端

package TCP通信流程.UDP丢包;

import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class TestReceive {
    public static void main(String[] args) {
        try {
            //数据的再次写入,利用本地字节输出流去完成
            FileOutputStream fileOutputStream =new FileOutputStream("");//这里做好是一个大点的文件或视频
            //指定接收端端口,用于发送端发送数据
            DatagramSocket datagramSocket=new DatagramSocket(9999);

            byte[]bytes=new byte[1024];

            while (true) {//死循环,会随着发送结束而结束

                DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
                //数据包中实际数据包大小
                int length = datagramPacket.getLength();

                //接受发送端发送的数据
                datagramSocket.receive(datagramPacket);

                //调用write方法写
                fileOutputStream.write(bytes, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

发送端

package TCP通信流程.UDP丢包;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class TestSend {
    public static void main(String[] args) {
        //本地读取
        try {
            //用于和接受端通信的socker
            DatagramSocket socket=new DatagramSocket();
            //本地直接输入流,读取本地文件,用于发送
            FileInputStream fileInputStream=new FileInputStream("D://");
            //构建字节数组用于读取
            byte[]bytes=new byte[100];//每次以100字节读取
            int len=0;
            //边读边写
            while ((len=fileInputStream.read(bytes)) != -1) {
                //以数据包的形式发送
                DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("localhost"),9999);

                socket.send(datagramPacket);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

解决方案

优化:
1.数据发慢一点
2.接收端做数据的回写记录,发送OK,才可以进行下一次的发送

优化一

1.数据发慢一点
接收端

package TCP通信流程.UDP丢包优化;

import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class TestReceive {
    public static void main(String[] args) {
        try {
            //数据的再次写入,利用本地字节输出流去完成
            FileOutputStream fileOutputStream =new FileOutputStream("");
            //指定接收端端口,用于发送端发送数据
            DatagramSocket datagramSocket=new DatagramSocket(9999);

            byte[]bytes=new byte[1024];

            while (true) {//死循环,会随着发送结束而结束

                DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
                //数据包中实际数据包大小
                int length = datagramPacket.getLength();

                //接受发送端发送的数据
                datagramSocket.receive(datagramPacket);

                //调用write方法写
                fileOutputStream.write(bytes, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

发送端

package TCP通信流程.UDP丢包优化;

import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/
 * 优化一
 *      数据发慢一些
 *
 * 缺点   是发送太慢
 */
public class TestSend {
    public static void main(String[] args) {
        //本地读取
        try {
            //用于和接受端通信的socker
            DatagramSocket socket=new DatagramSocket();
            //本地直接输入流,读取本地文件,用于发送
            FileInputStream fileInputStream=new FileInputStream("D://");
            //构建字节数组用于读取
            byte[]bytes=new byte[100];//每次以100字节读取
            int len=0;
            //边读边写
            while ((len=fileInputStream.read(bytes)) != -1) {
                //利用线程sleep,让数据发慢一点
                Thread.sleep(1);
                //以数据包的形式发送
                DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("localhost"),9999);
                
                socket.send(datagramPacket);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

优化二

接收端

package TCP通信流程.UDP丢包优化二;

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

/
 * 接收端

 * 缺点:
 *      会发生丢包现象
 * 优化
 *      1.数据发慢一点
 *      2.接收端做数据的回写记录,发送OK,才可以进行下一次的发送
 */
public class TestReceive {
    public static void main(String[] args) {
        try {
            //数据的再次写入,利用本地字节输出流去完成
            FileOutputStream fileOutputStream =new FileOutputStream("");
            //指定接收端端口,用于发送端发送数据
            DatagramSocket datagramSocket=new DatagramSocket(9999);

            byte[]bytes=new byte[1024];
            int count=0;

            while (true) {//死循环,会随着发送结束而结束

                DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
                //数据包中实际数据包大小
                int length = datagramPacket.getLength();

                //接受发送端发送的数据
                datagramSocket.receive(datagramPacket);

                //调用write方法写
                fileOutputStream.write(bytes, 0, length);

                count++;
                System.out.println("接受的数据包:"+count);

                //接收端发送数据
                DatagramPacket datagramPacket1=new DatagramPacket("ok".getBytes(),2, InetAddress.getByName("localhost"),8888);

                //调用socker发送数据
                datagramSocket.send(datagramPacket1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

发送端

package TCP通信流程.UDP丢包优化二;

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

/**
 *发送端
 *
 * 优化二:
    2.接收端做数据的回写记录,发送OK,才可以进行下一次的发送
 */
public class TestSend {
    public static void main(String[] args) {
        //本地读取
        try {
            //用于和接受端通信的socker
            DatagramSocket socket=new DatagramSocket(8888);//接受端回写数据包时所需端口号

            //本地直接输入流,读取本地文件,用于发送
            FileInputStream fileInputStream=new FileInputStream("D://");
            //构建字节数组用于读取
            byte[]bytes=new byte[100];//每次以100字节读取
            int len=0;
            int count=0;
            //边读边写
            while ((len=fileInputStream.read(bytes)) != -1) {
                //以数据包的形式发送
                DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length, InetAddress.getByName("localhost"),9999);

                socket.send(datagramPacket);

                count++;

                //以数据包的形式接受
                byte[]bytes1=new byte[2];
                DatagramPacket datagramPacket1 = new DatagramPacket(bytes1, bytes1.length);
                //接受回写数据
                socket.receive(datagramPacket1);
                String date=new String(bytes1);
                System.out.println(date);
                //是否是ok
                if (date.equals("ok")) {
                    continue;
                } else {

                }
            }
            System.out.println("发送端实际数据大小:"+count);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

URl类

URL 是统一资源定位符(Uniform Resource Locator)的简称,它表示 Internet 上某一资源的地址。通过 URL
用户可以访问各种网络资源,比如常见的 WWW 以及 FTP 站点。浏览器可以通过解析给定的 URL 在网络上查找相应的文件或其他资源。

案例

简单获取百度首页html

package TCP通信流程.URL;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;

/**
 *
 * 功能描述:
 * 基于URL统一资源定位器,去指定百度的首页,然后获取百度首页内容写到本地,以此来体验URL的作用
 * 1.通过URL指定域名资源
 * 2.通过openConnection()打开资源连接URLConnection 的连接conn
 * 3.通过URLConnection对象conn去常见字节输入流InputStre am的对象is
 * 4.使用is调用read()方法去读取百度首页的数据
 * 5.创建本地的字节输出流F ile0utStream对象fos
 * 6.使用fos对象将读到的数据写到本地
 * 7.关闭流
 *
 */
public class Connection {
    public static void main(String[] args) {
        try {
            //1.通过URL指定域名资源
            URL url=new URL("http://www.baidu.com");
            //2.通过openConnection()打开资源连接URLConnection 的连接conn
            URLConnection urlConnection = url.openConnection();
            //3.通过URLConnection对象conn去常见字节输入流InputStream的对象is
            InputStream is = urlConnection.getInputStream();
            byte[]bytes=new byte[100];
            int count=0;
            //5.创建本地的字节输出流F ile0utStream对象fos
            FileOutputStream fileOutputStream=new FileOutputStream("D://baidu.html");
            //4.使用is调用read()方法去读取百度首页的数据
            while ((count=is.read(bytes,0,10))!=-1){
                fileOutputStream.write(bytes,0,count);
            }
            //7.关闭流
            is.close();
            fileOutputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值