第1章 UDP與TCP協議
在介紹TCP/IP結構時,提到傳輸層的兩個重要的高級協議,分別是UDP和TCP,其中UDP是User Datagram Protocol的簡稱,稱為用戶數據報協議,TCP是Transmission Control Protocol的簡稱,稱為傳輸控制協議。
1.1 UDP協議
UDP是無連接通信協議,即在數據傳輸(數據大小在64kb以內)時,數據的發送端和接收端不建立邏輯連接。簡單來說,當一台計算機向另外一台計算機發送數據時,發送端不會確認接收端是否存在,就會發出數據,同樣接收端在收到數據時,也不會向發送端反饋是否收到數據。
由於使用UDP協議消耗資源小,通信效率高,所以通常都會用於音頻、視頻和普通數據的傳輸例如視頻會議都使用UDP協議,因為這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。
但是在使用UDP協議傳送數據時,由於UDP的面向無連接性,不能保證數據的完整性,因此在傳輸重要數據時不建議使用UDP協議。UDP的交換過程如下圖所示。
1.2 TCP協議
TCP協議是面向連接的通信協議,即在傳輸數據前先在發送端和接收端建立邏輯連接,然后再傳輸數據,它提供了兩台計算機之間可靠無差錯的數據傳輸。在TCP連接中必須要明確客戶端與服務器端,由客戶端向服務端發出連接請求,每次連接的創建都需要經過“三次握手”。第一次握手,客戶端向服務器端發出連接請求,等待服務器確認,第二次握手,服務器端向客戶端回送一個響應,通知客戶端收到了連接請求,第三次握手,客戶端再次向服務器端發送確認信息,確認連接。整個交互過程如下圖所示。
由於TCP協議的面向連接特性,它可以保證傳輸數據的安全性,所以是一個被廣泛采用的協議,例如在下載文件時,如果數據接收不完整,將會導致文件數據丟失而不能被打開,因此,下載文件時必須采用TCP協議。
第2章 UDP通信
2.1 DatagramPacket
前面介紹了UDP是一種面向無連接的協議,因此,在通信時發送端和接收端不用建立連接。UDP通信的過程就像是貨運公司在兩個碼頭間發送貨物一樣。在碼頭發送和接收貨物時都需要使用集裝箱來裝載貨物,UDP通信也是一樣,發送和接收的數據也需要使用“集裝箱”進行打包,為此JDK中提供了一個DatagramPacket類,該類的實例對象就相當於一個集裝箱,用於封裝UDP通信中發送或者接收的數據。
想要創建一個DatagramPacket對象,首先需要了解一下它的構造方法。在創建發送端和接收端的DatagramPacket對象時,使用的構造方法有所不同,接收端的構造方法只需要接收一個字節數組來存放接收到的數據,而發送端的構造方法不但要接收存放了發送數據的字節數組,還需要指定發送端IP地址和端口號。
接下來根據API文檔的內容,對DatagramPacket的構造方法進行逐一詳細地講解。
使用該構造方法在創建DatagramPacket對象時,指定了封裝數據的字節數組和數據的大小,沒有指定IP地址和端口號。很明顯,這樣的對象只能用於接收端,不能用於發送端。因為發送端一定要明確指出數據的目的地(ip地址和端口號),而接收端不需要明確知道數據的來源,只需要接收到數據即可。
使用該構造方法在創建DatagramPacket對象時,不僅指定了封裝數據的字節數組和數據的大小,還指定了數據包的目標IP地址(addr)和端口號(port)。該對象通常用於發送端,因為在發送數據時必須指定接收端的IP地址和端口號,就好像發送貨物的集裝箱上面必須標明接收人的地址一樣。
上面我們講解了DatagramPacket的構造方法,接下來對DatagramPacket類中的常用方法進行詳細地講解,如下表所示。
2.2 DatagramSocket
DatagramPacket數據包的作用就如同是“集裝箱”,可以將發送端或者接收端的數據封裝起來。然而運輸貨物只有“集裝箱”是不夠的,還需要有碼頭。在程序中需要實現通信只有DatagramPacket數據包也同樣不行,為此JDK中提供的一個DatagramSocket類。DatagramSocket類的作用就類似於碼頭,使用這個類的實例對象就可以發送和接收DatagramPacket數據包,發送數據的過程如下圖所示。
在創建發送端和接收端的DatagramSocket對象時,使用的構造方法也有所不同,下面對DatagramSocket類中常用的構造方法進行講解。
該構造方法用於創建發送端的DatagramSocket對象,在創建DatagramSocket對象時,並沒有指定端口號,此時,系統會分配一個沒有被其它網絡程序所使用的端口號。
該構造方法既可用於創建接收端的DatagramSocket對象,又可以創建發送端的DatagramSocket對象,在創建接收端的DatagramSocket對象時,必須要指定一個端口號,這樣就可以監聽指定的端口。
上面我們講解了DatagramSocket的構造方法,接下來對DatagramSocket類中的常用方法進行詳細地講解。
2.3 UDP網絡程序
講解了DatagramPacket和DatagramSocket的作用,接下來通過一個案例來學習一下它們在程序中的具體用法。
下圖為UDP發送端與接收端交互圖解
要實現UDP通信需要創建一個發送端程序和一個接收端程序,很明顯,在通信時只有接收端程序先運行,才能避免因發送端發送的數據無法接收,而造成數據丟失。因此,首先需要來完成接收端程序的編寫。
l UDP完成數據的發送
/** * 實現UDP協議的發送端 * 實現封裝數據的類java.net.DatagramPacket 將你的數據包裝 * 實現封裝數據的類java.net.DatagramSocket 將你的數據發送 * * 實現步驟: * 1.創建DatagramPacket對象,封裝數據,接收端的地址和端口 * 2.創建DatagramSocket對象 * 3.調用DatagramSocket類方法send,發送數據包 * 4.關閉資源DatagramSocket * * Created by YuKai Fan on 2018/8/13. */ public class UDPSend2 { public static void main(String[] args) throws IOException { //創建數據包對象,封裝要發送的數據,接收端IP,端口 Scanner sc = new Scanner(System.in); //創建DatagramSocket對象 DatagramSocket ds = new DatagramSocket(); //創建InetAddress對象,封裝自己的ip地址 InetAddress inet = InetAddress.getByName("127.0.0.1"); while(true) { String message = sc.nextLine(); byte[] data = message.getBytes(); DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000); //調用ds方法send,發送數據包 ds.send(dp); } //關閉資源 //ds.close(); } }
l UDP完成數據的接收
/** * 實現UDP接收端 * 實現封裝數據的類java.net.DatagramPacket 將你的數據接收 * 實現輸出數據的類java.net.DatagramSocket 接收數據包 * * 實現步驟: * 1.創建DatagramSocket對象,綁定端口號,要和發送端端口號一致 * 2.創建字節數據,接收發來的數據 * 3.創建數據包對象DatagramPacket * 4.調用DatagramSocket對象方法receive(DatagramPacket dp)接收數據,數據放在數據包中 * 5.拆包 * 發送的ip地址 * 數據包對象DatagramPacket方法getAddress()獲取的是發送端IP地址 * 接收到的字節個數 * 數據包對象DatagramPacket對象中的方法getLength() * 發送方的端口號 * 數據包對象DatagramPacket方法getPort()獲取的是發送端端口 * 6.關閉資源 * * 永不停歇接收端 * Created by YuKai Fan on 2018/8/13. */ public class UDPReceieve2 { public static void main(String[] args) throws IOException { //創建數據包傳輸對象DatagramSocket,綁定端口號 DatagramSocket ds = new DatagramSocket(6000); //創建字節數組 byte[] data = new byte[1024]; //創建數據包對象,傳遞字節數組 while(true) { DatagramPacket dp = new DatagramPacket(data,data.length); //調用ds對象的方法receive ds.receive(dp);//receive具有線程等待 //獲取發送端的ip地址對象 String ip = dp.getAddress().getHostAddress(); //獲取發送的端口號 int port = dp.getPort(); //獲取接收到的字節個數 int length = dp.getLength(); System.out.println(new String(data,0,length)+"..."+ip+":"+port); } //ds.close(); } }