Java 中UDP原理机制及实现方式介绍(建议阅读者阅读前了解下Java的基础知识,一方便理解)

1.基本概念介绍:

首先得简单介绍下UDP。

UDP( User Datagram Protocol )协议是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层——传输层,处于IP协议的上一层。它是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的 但是这种协议却是方便快捷的,因此很多通信工具和游戏仍然采用这种通信方式,虽然有时会出现数据丢帧的现象。

此处可以简单的理解为某些无良快递机构,因为缺乏责任心,只负责发送至于包裹是否能顺利送达目的地毫不关心,因此会出现丢包或者延迟接收的现象)。

在Java中操纵UDP 使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket类,可以非常方便地控制用户数据报文进行UDP的程序开发。

在UDP开发中使用DatagramPacket类来包装一条需要发送的信息,之后使用DatagramSocket类用于完成信息的发送操作。

一个完整的UDP网络开发程序是包含服务器端和客户端的。

关于UDP开发中的服务器和客户端的区别:

  客户端与服务器端的唯一区别在于:服务器端的IP地址、端口是固定的,所以客户端可以直接将该数据报发送给服务器端,而服务器端则需要根据接收到的数据报来决定"反馈"数据报的目的地。

下面简单介绍下 DatagramSocket和DatagramPacket类的常用方法。

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

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

注意:1.在创建DatagramSocket类实例时,如果端口已经被使用,会产生一个SocketException的异常抛出,并导致程序非法终止,这个异常应该注意捕获。

DatagramPacket类:用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。 
DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从buf数组中,取出length长的数据创建数据包对象,目标是addr地址,port端口。 
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int
port):从buf数组中,取出offset开始的、length长的数据创建数据包对象,目标是addr地址,port端口。 
DatagramPacket(byte[] buf, int offset, int length):将数据包中从offset开始、length长的数据装进buf数组。
DatagramPacket(byte[] buf, int length):将数据包中length长的数据装进buf数组。 
getData():它从实例中取得报文的byte数组编码

  

2.实现方法:

想要实现UDP程序,建议首先从客户端编写,在客户端指定需要接收的端口和取得数据。

客户端(接收端)实现步骤
1. 建立udp的socket服务。要监听一个端口。 DatagramSocket ds = new
DatagramSocket(9001);
2. 定义一个缓冲区,将该缓冲区封装到packet包中。 byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
3. 通过socket的receive方法将数据存入数据包中。 ds.receive(dp);
4. 通过数据包dp的方法getData()、getAddress()、getPort()等方法获取包中的指定信息。
5. 关闭socket。 ds.close();

请看以下的代码以下是UDP的客户端程序:

import java.net.DatagramPacket ;

import java.net.DatagramSocket ;

public class UDPClient{

         public static void main(String args[]) throws Exception{     // 所有异常抛出

                 DatagramSocket ds = null ;                  // 定义接收数据报的对象

                 byte[] buf = new byte[1024] ;      // 开辟空间,以接收数据

                 DatagramPacket dp = null ;                  // 声明DatagramPacket对象

                 ds = new DatagramSocket(9000) ;    // 客户端在9000端口上等待服务器发送信息

                 dp = new DatagramPacket(buf,1024) ; // 所有的信息使用buf保存

                 ds.receive(dp)  ; // 接收数据

                 String str = new String(dp.getData(),0,dp.getLength()) + "from " +

                          dp.getAddress().getHostAddress() + ":" + dp.getPort() ;

                 System.out.println(str) ; // 输出内容

         }

};

以上程序运行后,客户端程序已经打开了监听的端口,等待服务器端向客户端发送信息。

下面开始介绍服务器端(发送端)实现步骤
1. 建立udpsocket服务端点。该端点建立,系统会随机分配一个端口。如果不想随机配置,可以手动指定。 DatagramSocket ds = new
DatagramSocket(3000);

2. 将数据进行packet包的封装,必须要指定目的地地址和端口。  byte[] buf = "hi 红军".getBytes(); DatagramPacket
dp =new
DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),9000);

3. 通过socket服务的send方法将该包发出。 ds.send(dp);

4. 将socket服务关闭。主要是关闭资源。 ds.close();

下面开始编写UDP的发送 服务器程序—Udpserve

import java.net.DatagramPacket ;

import java.net.DatagramSocket ;

import java.net.InetAddress ;

public class UDPServer{

         public static void main(String args[]) throws Exception{     // 所有异常抛出

                 DatagramSocket ds = null ;                  // 定义发送数据报的对象

                 DatagramPacket dp = null ;                  // 声明DatagramPacket对象

                 ds = new DatagramSocket(3000) ;    // 服务端在3000端口上等待服务器发送信息\

                 String str = "hello World!!!" ;

                 dp = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),9000) ; // 所有的信息使用buf保存

                 System.out.println("发送信息。") ;

                 ds.send(dp);     // 发送信息出去

                 ds.close() ;

         }

};

服务器端程序运行后,客户端就可以接收服务器端发送来的数据了。

以上是一个简单的收发过程。当然为了保证每个设备都可以收发,可以同时运行服务器和客户端程序。

从以上程序我们可以看出使用DatagramSocket进行网络通信时,服务器端无须也无法保存每个客户端的状态,客户端把数据报发送到服务器端后,完全有可能立即退出。但不管客户端是否退出,服务器端都无法知道客户端的状态。

当使用UDP协议时,如果想让一个客户端发送的聊天信息被转发到其他所有的客户端则比较困难,可以考虑在服务器端使用Set集合来保存所有的客户端 信息,每当接收到一个客户端的数据报之后,程序检查该数据报的源SocketAddress是否在Set集合中,如果不在就将该 SocketAddress添加到该Set集合中。这样又涉及一个问题:可能有些客户端发送一个数据报之后永久性地退出了程序,但服务器端还将该客户端的 SocketAddress保存在Set集合中……总之,这种方式需要处理的问题比较多,编程比较烦琐。

基于UDP数据传输特性,它的不可靠性也给我们在开发过程的带来了麻烦,针对此类问题,提出以下解决方案:

服务器和客户端 可以建立一套自己的校验方案(方案形式很多例如:XML,校验和等检验方式),如果数据包丢失造成数据不完整,采用补发的形式来完成,当然这个方案类似于TCP的握手连接。

在开发过程中还有很多细节,文章摘取网络上一些信息。文笔不好,还望见谅,此为普及类文章,希望能带给大家帮助。如若有发现什么问题,请及时指出方便我修改。谢谢。

张敬宇

2015.3.15于南京编辑

时间: 2024-10-08 22:55:55

Java 中UDP原理机制及实现方式介绍(建议阅读者阅读前了解下Java的基础知识,一方便理解)的相关文章

JAVA中单例模式的几种实现方式

1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线 程安全就不会使用这种方式.但是如果不需要保证线程安全,则这种方式还是不错的,因为所需要的开销比较小.下面是具体的实现代码: 转http://www.cnblogs.com/CodeGuy/p/3580486.html public Class Singleton { private static Singleton instance

Java中的异常处理机制的简单原理和应用。

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间.地点.人物.情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息. Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Erro

黑马程序员【java中的反射机制】

Java中的反射机制 ------- android培训.java培训.期待与您交流! ---------- java的反射机制是java的特性之一,反射机制是构建框架技术的基础所在,使用反射可以使程序更加灵活,避免将程序写死在代码里.相对于很多初学者只接触过java基础的人,反射还是一个很朦胧难懂的概念,下面我们就来说一下反射的一些应用. java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能.java反射有3个动态性质:1.运行时生成对象实例,2.运行期间调用发放,3.运行

JAVA中关于锁机制

本文转自 http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁

java中的"包"机制

完全掌握java中的"包"机制 "包"机制是java中特有的,也是java中最基础的知识.一些初学java的朋友,通常象学其它语言一样从教材上copy一些程序来运行,可是却常常遇到莫名其妙的错误提示.这些问题事实上都出在对"包"的原理不够清楚.本文将就此问题进行深入阐述. 一.为什么java中要有"包"的概念? 以一言概之,java中"包"的引入的主要原因是java本身跨平台特性的需求.因为java中的所有

Java中的异常处理机制

Java中的异常处理机制 示例 public class test { public static void main(String[] args) { // TODO Auto-generated method stub String s="hello"; int i=Integer.parseInt(s); } } 运行异常结果 在上述代码中Integer.parseInt表示把字符串类型转化成整数类型,同时Integer是Int的封装类:程序会报错,因为计算机没有办法把hello

图解JAVA中的类加载机制(详细版)

注:本文为作者整理和原创,如有转载,请注明出处. 上一篇博文,把JAVA中的Class文件格式用图形的方式画了一下,逻辑感觉清晰多了,同时,也为以后查阅的方便. Class文件只是一种静态格式的二进制流,它只有被虚拟机加载进内存解析之后才会生成真正的运行时的结构,因此,搞清楚类加载机制不但有助于我们加深理解Class文件中各个字段的含义,同时也有利于我们更深入的了解JAVA代码背后的暗流涌动.比如new关键字背后,虚拟机都做了什么?JAVA中的哪些操作会真正导致类被加载?哪些操作又会导致类被初始

浅说Java中的反射机制(二)

写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编译执行java文件时,会生成一个.class文件,反射就是一个反编译的过程,它可以通过.class文件得到一个java对象.一个类会有很多组成部分,比如成员变量.成员方法.构造方法等,反射可以通过加载类(加载类是个什么东西?一直搞不清楚),解剖出类的各个组成部分. 为什么要用反射? 我们需要访问一个

Java中的参数传递机制

通过前一篇文章的介绍,我们从整体上明白了,Java类中变量的差异性.不同变量在内存中的存储位置,以及变量的生命周期等.今天,我们来看一下Java中参数传递的机制. 形参:方法声明时包含的参数声明 实参:调用方法时,实际传给形参的参数值 Java方法的参数传递机制: Java方法的参数传递只有一种:值传递.所谓值传递,就是将实际参数值的副本,传入方法内,而参数本身不会收到任何影响. PS:传入方法的时实际参数值的复制品,不管方法中对这个复制品如何操作,实际参数本身不会受到任何影响. 基本类型的参数