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于南京编辑

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

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

    1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...

  2. Java中HashMap遍历的两种方式

    Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...

  3. 夯实Java基础系列11:深入理解Java中的回调机制

    目录 模块间的调用 多线程中的"回调" Java回调机制实战 实例一 : 同步调用 实例二:由浅入深 实例三:Tom做题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 ...

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

    写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...

  5. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  6. 【Java基础】java中的反射机制与动态代理

    一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...

  7. 【夯实基础】- Java中的fail-fast机制

    转载自:Java中的fail-fast机制 遍历删除List中的元素有很多种方法,当运用不当的时候就会产生问题.下面主要看看以下几种遍历删除List中元素的形式: 1.通过普通的for删除删除符合条件 ...

  8. 结合实战和源码来聊聊Java中的SPI机制?

    写在前面 SPI机制能够非常方便的为某个接口动态指定其实现类,在某种程度上,这也是某些框架具有高度可扩展性的基础.今天,我们就从源码级别深入探讨下Java中的SPI机制. 注:文章已收录到:https ...

  9. java中的反射机制在Android开发中的用处

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...

随机推荐

  1. tableview 在ios8上面分割线不全的问题

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath ...

  2. cocos2d-x系列 Mac下配置cocos2d-x开发环境(android和ios)

    一.下载cocos2d-x http://cocos2d-x.org/projects/cocos2d-x/wiki/Download cocos2d-x-2.1.4.zip @ June.18, 2 ...

  3. 一天学完UFLDL

    学习UFLDL笔记 第一节 神经网络 神经元长这样 大写W看着有点不习惯.. 激活函数, 就是上面式子中的f. 可以选 sigmoid函数(或者叫 logistic回归,对数几率函数),反正就是这样一 ...

  4. Codeforces 549B Looksery Party

    Looksery Party Solution: 仔细分析一下会发现每个人都会发一条消息给自己这个条件非常重要! 这个条件保证了一定会有解,而且解法也要从这里入手. 当我们拿到一个猜测的答案序列的时候 ...

  5. angularjs应用骨架

    使用典型的类库时,你可以选择并使用你所喜欢的功能:而对于angularjs框架来说,必须把它看成一个完整的套件来使用,框架中的所有的东西都包含在里面,接下来将会介绍angular的基础模块,这样你就可 ...

  6. web版扫雷小游戏(三)

    ~~~接上篇,上篇介绍了游戏实现过程中第一个比较繁琐的地方,现在展现在玩家面前的是一个有血有肉的棋盘,从某种意义上说玩家已经可以开始游戏了,但是不够人性化,玩家只能一个一个节点的点开,然后判断,然后标 ...

  7. qrcode.js插件将你的内容转换成二维码格式

    ---qrcode.js插件将你的内容转换成二维码格式--- 我之前一直想知道二维码是怎么生成,所以就了解了一下, 最后还是不知道它的原理, 但是,我知道怎么生成. 现在就让我带你制作一个你喜爱的二维 ...

  8. 11-18的学习总结(DOMSecondday)

    DOM:读取访问节点对象属性 批量删除父元素下所有子节点 elem.innerHTML=""; 批量替换父元素下所有子节点 elem.innerHTML="所有子元素标签 ...

  9. 主流的phpcms分析

    小型网站适合wordpress,onethink,joomla(囧啦)    wordpress(免费开源) 优点:1.样式丰富,模板重多 2. 安全性 3. 对搜索引擎友好,收录快.        ...

  10. php 获取客户端IP地址

    /** * 获取真实IP地址 */ /* 在PHP中getenv(参数)函数是一个用于获取环境变量的函数,根据提供不同的参数可以获取不同的环境变量, getenv("REMOTE_ADDR& ...