java网络编程(3)UDP协议编程(单播多播广播)

简介: 这一篇文章开始着重讲解UDP编程。这块的知识也算是非常重要的,而且现在的编程都离不开网络。花了一些时间整理了一下。

一、基本认识


1、什么是UDP协议?

UDP协议,也就是用户数据报协议(User Datagram Protocol),是一个简单的面向数据报的传输层协议。只在IP协议上增加了很少一点的功能,就是复用和分用,以及差错检测的功能。


特点我们可以整理总结一下:


(1)无连接:也就是说发送之前不需要建立连接,直接发送就可以,和TCP协议相比就减少了三次握手四次挥手等时间的消耗。

(2)不可靠交付:也就是说,我们只管发送数据,对方收没收到不需要去管。

(3)面向报文:只进行简单的添加首部数据,就直接封装成IP包发送了。

(4)支持多对多:这里表示的就是单播多播广播机制。

(5)没有拥塞控制


2、数据格式


在上面我们知道,UDP协议包只在I协议上增加了很少一点的功能,就是复用和分用,以及差错检测的功能。那添加的这些数据是什么样子的呢?

UDP协议分为首部字段和数据字段,其中首部字段只占用8个字节,分别是个占用两个字节的源端口、目的端口、长度和检验和。

v2-98b28f5777756a6b126cf9d457d1d8b9_1440w.jpg

我们在这里对添加的数据字段分别进行一个解释说明:


(1)伪首部:伪首部其实是在校验和的时候添加的,起到一个辅助运算的作用。并不是真正的首部。校验和是为了检查报文中的数据是否有差错,如果没有差错,那么校验和之后的结果应该是1.

(2)源端口:源端口,这是为了收到对方的回音才使用的。

(3)目的端口:也就是UDP数据包的发送目的地。

(4)长度:数据长度

(5)检验和:检查是否数据出现了差错,如果有差错,那就丢弃。


3、UDP协议能做什么?


在UDP协议之上的协议相信我们都听过,比如说DNS、TFTP、SNMP等等,这些协议在网络通信中非常的实用也非常的重要。而且像视频、音频、和一些无关紧要的数据都可以使用他来发送,省时省力。


有一个非常重要的例子,那就是我们的微信聊天的场景,他就是采用的UDP协议,因为UDP协议是不可靠协议,你只管发送就好了,不管对方是否收到信息。对方有时间就会看到这条消息。


4、通信方式


在java中,UDP通信方式主要有三种:单播、多播和广播。

v2-a05eef6754e38a726d6b99bb7c1bdd81_1440w.jpg

1)单播:每次只有两个主机在通信。

在IPv4网络中,0.0.0.0到223.255.255.255属于单播地址。就好比说你在大街上叫你女朋友名字,那么就只有你女朋友回头。


(2)广播:当前主机和当前局域网下所有的主机通信

广播肯定都是限制在局域网中的,因为你要是朝着整个互联网广播一条消息,那实在是太麻烦了,而且很多人广播的时候会造成网络堵塞,因此只限定在局域网中。这种方式也很好理解,就好比你在大街上叫了一声“美女帅哥”,那么周围(局域网)所有的人都会回头。

他的地址一般都是255.255.255.255。另外ipv6不支持广播。


(3)多播:当前主机和当前局域网下一部分主机通信

多播也很好理解,就比如你在大街上只喊了一句美女,没有喊帅哥,那么就只有美女回头,帅哥不会回头。组播的地址就比较麻烦一点了。因为你可以把你要通信的地址汇聚到一块形成一个多播组。但是有些地址是官方已经限定好了的,你就没法使用,这叫做永久组。还有一些没被使用的地址就组成了临时组。


其中永久的组播地址:224.0.0.0-224.0.0.2。而剩下的就是临时组了:224.0.1.0~224.0.1.255是公用组播地址

下面我们着重使用代码来实现一下这三种通信方式:


二、代码实现


1、单播案例


单播案例很简单,在这里我们假设,你在大街上叫你女朋友名字,然后你女朋友回头答复了你一句。


首先看一下服务端:代表女朋友:

class UDPServer{
    public static void main(String[] args)throws IOException{
        //新建一个socket绑定8888端口
        DatagramSocket  server = new DatagramSocket(8888);
        //接收消息
        byte[] recvBuf = new byte[100];
        DatagramPacket recvPacket  = new DatagramPacket(recvBuf , recvBuf.length);
        server.receive(recvPacket);
        String recvStr = new String(recvPacket.getData() , 0 , recvPacket.getLength());
        System.out.println("来自男朋友的呼唤;" + recvStr);
        //发送消息:根据接受的port确定男朋友在哪
        int port = recvPacket.getPort();
        InetAddress addr = recvPacket.getAddress();
        String sendStr = "不想回复你,只想买衣服";
        byte[] sendBuf;
        sendBuf = sendStr.getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendBuf,sendBuf.length ,addr,port);
        server.send(sendPacket);
        server.close();
    }
 }

然后就是客户端了

class UDPClient{
    public static void main(String[] args)throws IOException{
        DatagramSocket client = new DatagramSocket();
        //发送数据
        String sendStr = "Hello!王xx";
        byte[] sendBuf;
        sendBuf = sendStr.getBytes();
        InetAddress addr = InetAddress.getByName("127.0.0.1");
        int port = 8888;
        DatagramPacket sendPacket = new DatagramPacket(sendBuf ,sendBuf.length , addr , port);
        client.send(sendPacket);
        //接受数据
        byte[] recvBuf = new byte[100];
        DatagramPacket recvPacket = new DatagramPacket(recvBuf , recvBuf.length);
        client.receive(recvPacket);
        String recvStr = new String(recvPacket.getData() , 0 ,recvPacket.getLength());
        System.out.println("收到女朋友的回复:" + recvStr);
        client.close();
     }
}

代码很简单,你运行一下就能体会到,在这里就不显示结果了。在这里我们会发现里面主要涉及到了两个类DatagramSocket和DatagramPacket。分别表示socket和数据包。这一点和广播涉及到的类是一样的。


2、广播


在大街上,你突然喊了一句,帅哥美女们,于是乎都回头了。

首先是服务端:帅哥美女们

class UDPServer{
    public static void main(String[] args) {
           int port = 9999;//开启监听的端口
           DatagramSocket ds = null;
           DatagramPacket dp = null;
           byte[] buf = new byte[1024];//存储发来的消息
           StringBuffer sbuf = new StringBuffer();
           try {
               //绑定端口的
               ds = new DatagramSocket(port);
               dp = new DatagramPacket(buf, buf.length);
               System.out.println("街上的帅哥美女们都准备好了:");
               ds.receive(dp);
               ds.close();
               int i;
               for(i=0;i<1024;i++){
                   if(buf[i] == 0){
                       break;
                   }
                   sbuf.append((char) buf[i]);
               }           
               System.out.println("听到街上有人说:" + sbuf.toString());
           }
           catch (Exception e) {
               e.printStackTrace();
           } 
       }
 }

然后就是客户端:

class UDPClient{
    public static void main(String[] args)throws IOException{
        String host = "255.255.255.255";//广播地址
        int port = 9999;//广播的目的端口
        String message = "hello girl and boy";//用于发送的字符串
        try{
            InetAddress adds = InetAddress.getByName(host);
            DatagramSocket ds = new DatagramSocket();
            DatagramPacket dp = new DatagramPacket(message.getBytes(),message.length(), adds, port);
            ds.send(dp);
            ds.close();
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
     }
}

完整的代码都在这了,对于结果自己运行一下吧。


3、多播


在大街上,你突然喊了一句美女,于是乎街上的美女们就都回头了,但是帅哥却不会。

首先是服务端:美女

class UDPServer {
    private static MulticastSocket ds;
    static String multicastHost = "239.0.1.255";
    static InetAddress receiveAddress;
    public static void main(String[] args) throws IOException {
        ds = new MulticastSocket(8899);
        // 也就是只接受239.0.1.255这个地址的人发来的消息
        receiveAddress = InetAddress.getByName(multicastHost);
        // 加入多播组
        ds.joinGroup(receiveAddress);
        //在线程里面处理信息
        new Thread() {
            public void run() {
                byte buf[] = new byte[1024];
                DatagramPacket dp = new DatagramPacket(buf, 1024);
                while (true) {
                    try {
                        System.out.println("美女(没有帅哥)准备好了:");
                        ds.receive(dp);
                        String receiveMsg=new String(buf, 0, dp.getLength());
                        System.out.println("美女听到街上有人喊:" + receiveMsg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

然后是客户端

class UDPClient{
    public static void main(String[] args) throws IOException{
        MulticastSocket ms=null; 
        DatagramPacket dataPacket = null; 
        ms = new MulticastSocket();
        ms.setTimeToLive(32);  
        //将本机的IP地址放到数据包里
         byte[] data = "街上的美女们".getBytes();   
         InetAddress address = InetAddress.getByName("239.0.1.255");   
         dataPacket = new DatagramPacket(data, data.length, address,8899);  
         ms.send(dataPacket);  
         ms.close();   
    }
}

可能你已经发现了,DatagramSocket已经变成了MulticastSocket,说明多播有自己的实现机制。如果你想进一步了解,可以深入其源码看看。


OK。java中UDP编程基本上就是这三种方式,基本案例在此,你可以根据自己的需要变形。

相关文章
|
11天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
17 2
|
7天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
9天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
91 53
|
8天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
5天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
7天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
19 2
|
8天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
36 1
|
12天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
11天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。