Java 中UDP原理機制及實現方式介紹(建議閱讀者閱讀前瞭解下Java的基礎知識,一方便理解)

1.基本概念介紹:java

首先得簡單介紹下UDP。編程

UDP( User Datagram Protocol )協議是用戶數據報,在網絡中它與TCP協議同樣用於處理數據包。在OSI模型中,在第四層——傳輸層,處於IP協議的上一層。它是一種無鏈接的協議,每一個數據報都是一個獨立的信息,包括完整的源或目的地址,它在網絡上以任何可能的路徑傳往目的地,所以可否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的 可是這種協議倒是方便快捷的,所以不少通訊工具和遊戲仍然採用這種通訊方式,雖然有時會出現數據丟幀的現象。數組

此處能夠簡單的理解爲某些無良快遞機構,由於缺少責任心,只負責發送至於包裹是否能順利送達目的地絕不關心,所以會出現丟包或者延遲接收的現象)。服務器

在Java中操縱UDP 使用位於JDK中Java.net包下的DatagramSocket和DatagramPacket類,能夠很是方便地控制用戶數據報文進行UDP的程序開發。網絡

在UDP開發中使用DatagramPacket類來包裝一條須要發送的信息,以後使用DatagramSocket類用於完成信息的發送操做。socket

一個完整的UDP網絡開發程序是包含服務器端和客戶端的。工具

關於UDP開發中的服務器和客戶端的區別:編碼

  客戶端與服務器端的惟一區別在於:服務器端的IP地址、端口是固定的,因此客戶端能夠直接將該數據報發送給服務器端,而服務器端則須要根據接收到的數據報來決定"反饋"數據報的目的地。spa

下面簡單介紹下 DatagramSocket和DatagramPacket類的經常使用方法。.net

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於南京編輯

相關文章
相關標籤/搜索