標籤:
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, intport):從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 = newDatagramSocket(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 = newDatagramSocket(3000);
2. 將資料進行packet包的封裝,必須要指定目的地地址和連接埠。 byte[] buf = "hi 紅軍".getBytes(); DatagramPacketdp =newDatagramPacket(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的基礎知識,一方便理解)