聊聊UDP、TCP和實現一個簡單的JAVA UDP小Demo
最近真的比較忙,很久就想寫了,可是一直苦於寫點什麼,今天腦袋靈光一閃,覺得自己再UDP方面還有些不了解的地方,所以要給自己掃盲。
好了,咱們進入今天的主題,先列一下提綱:
1. UDP是什麼,UDP適用於什麼場景?
2. 寫一個小Demo來加深一下UDP的理解。
3. UDP和TCP的區別有哪些?
4. TCP建連和關閉的過程,為什麼建立連接的時候是三次握手,斷開連接的時候需要四次?
1. UDP是什麼,UDP適用於什麼場景?
相信很多同學都聽過UDP,UDP的全稱:User Datagrame Protocol, 用戶報文協議,是一個傳輸層協議。UDP最大的特點是:不可靠網路傳輸,無連接數據協議,即發送前不要連接,直接向目標地址發送。而TCP和UDP基本上是相互補充的,TCP是可靠的數據數據傳輸,基於連接後的數據發送。
TCP是Transmission Control Protocol,傳輸控制協議,TCP是基於可靠的數據傳輸,那麼就需要犧牲更多的延遲和網路頻寬。而UDP則不需要可靠的數據傳輸,那麼將會需要更小的網路延遲和網路開銷。UDP可以允許丟棄延遲的數據包。由於低延遲低頻寬,所以UDP非常適合電腦遊戲,語音電話,影片電話,網路直播。
我們接下來看一下UDP的Packet的組成(圖片來源網路),8位元組的Header,然後就是UDP的數據。本機如果作為客戶端的話,本機的埠號為0-65535,也就是本機連接外部機器的話最多可以連接65536,0是保留埠號。如果作為服務端的話,可以使用的埠為2的32次方個埠。也就是可以接收的數據可以有這麼多。當然,目前一台機器能處理的數據沒有這麼多。
8位元組的Header,很簡單也比較少,不像TCP需要20-60位元組的數據。
Source port,源埠號,16位2個位元組。
Length, 數據的長度2個位元組。
Distination port, 目標埠,用於識別到目標機器的埠號。2個位元組。
Checksum, 用於計算Header的Checksum(校驗值)。
2. 寫一個小Demo來加深一下UDP的理解。
1) UDP的服務端程式碼,因為UDP的程式碼都是JDK自帶的,所以也不需要引入其他jar包就可以。
2)Server端主要創建步驟:
a) 創建一個監聽udp的埠號 8888.
b) 創建一個用於接收數據的DatagramPacket,參數有兩個,一個是數據,一個是數據的長度。
c) 採用循環進行receive數據,直到收到的bye字元串。
package myflink.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * @author huangqingshi * @Date 2020-05-24 */ public class UDPServer { public static void main(String[] args) throws IOException { //1. 創建一個監聽8888埠的udp socket DatagramSocket ds = new DatagramSocket(8888); //設置接收數據的最大值 byte[] receive = new byte[65535]; //用於接收的數據 DatagramPacket datagramPacket = null; while(true) { //2. 創建一個用於接收數據。buf即數據和其長度 datagramPacket = new DatagramPacket(receive,receive.length ); //3. 接收byteBuff的數據 ds.receive(datagramPacket); System.out.println("Client:-" + data(receive)); //4. 如果接收到了bye,程式結束 if("bye".equals(data(receive))) { break; } //5.清理receive中的數據 receive = new byte[65535]; } } public static StringBuilder data(byte[] bytes) { if(bytes == null) { return null; } StringBuilder ret = new StringBuilder(); int i = 0; while (bytes[i] != 0) { ret.append((char) bytes[i]); i++; } return ret; } }
3)接下來是客戶端的程式碼,步驟如下:
a) 創建scanner用於在控制台進行數據輸入,然後創建一個DatagramSocket用於處理數據。
b) 創建一個DatagramPacket用於數據的發送。
c) 進行數據發送。
d) 持續發送數據,當收到bye字元串的話就會結束。
package myflink.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; /** * @author huangqingshi * @Date 2020-05-24 */ public class UDPClient { public static void main(String[] args) throws IOException { Scanner scanner = new Scanner(System.in); InetAddress ip = InetAddress.getLocalHost(); //1. 創建一個socket對象用於處理數據 DatagramSocket socket = new DatagramSocket(); //用於存放數據 byte[] buf = null; //一個死循環,用於接收數據後處理,收到bye後結束處理 while(true) { String input = scanner.nextLine(); //將接收到的資訊轉換為byte數組 buf = input.getBytes(); //2. 創建一個DatagramPack包用於創建發送的數據 DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, ip, 8888); //3. 發送數據 socket.send(datagramPacket); //4. 如果收到了byte直接結束循環 if("bye".equals(input)) { break; } } } }
啟動服務端和客戶端,然後再控制台輸入一些測試數據,看一下服務端的控制台輸出:
客戶端輸入數據
Hello UDP
服務端的輸出數據
Client:-Hello UDP
好了Demo已經執行完了,非常簡單。
3. UDP和TCP的區別有哪些?
1. TCP是可靠的傳輸,而UDP是非可靠數據傳輸。因為可靠,所以需要更高的延遲和網路頻寬。而UDP則不需要,所以比較適合語音、影片電話等。
2. UDP的Header位元組為8個位元組,非常少,而TCP需要至少20個位元組。
3. TCP是全雙工的,即可以發送接收數據,可以想像兩個人打電話,即可以聽到聲音又可以發送聲音。而UDP發送數據的時候才連接,發送完數據之後不會保留連接。
4. TCP是點對點連接的,而UDP是多對多進行數據傳輸。TCP以位元組流形式發送,有擁塞控制,方式發送數據量太大擁塞。而UDP是以報文形式發送給目標機器,沒有擁塞控制。
4. TCP建連和關閉的過程,為什麼建立連接的時候是三次握手,斷開連接的時候需要四次?
1)三次握手建立連接處理:
1. 首先創建連接時client需要發送一個SYN+隨機sequence給 server 端,這是客戶端的狀態是SYN_SENT狀態。
2. server收到數據後會回復一個SYN+ACK,ACK為接收到的sequence+1,同時再發送一個sequence。server的狀態為ACK_REVD。
3. client再把收到的sequnce+1作為ACK再給到服務端,然後服務端和客戶端的狀態都是ESTABLISHED。說明連接建立了。
第二步中的SYN和ACK可以同時發送,這兩個值同時發送不受影響,都可以建立連接。當然如果兩步分開發送也是可以的,但是由於可以節省一步發送,所以不用多費事。
2)四次握手關閉連接處理:
1. client發一個FIN和一個隨機的sequence給server,然後客戶端的狀態變為FIN_WAIT_1狀態。
2. server收到了FIN後,狀態變為CLOSE_WAIT,然後再把接收到的sequence+1和ACK標誌返回給client。client收到ACK後變為FIN_WAIT2狀態。
3. 然後server再次給client發送一個FIN+sequence給client,此時客戶端的狀態變為TIME_WAIT狀態。
4. client再把收到的sequence + 1發送給server, 此時server的狀態變為CLOSED。此時連接正式斷開。
這是客戶端主動發起關閉連接的過程,還有同時發送FIN標誌的情況。
1. client發送FIN+sequence給server端,狀態變為FIN_WAIT_1。
2. server也發送FIN+sequence給client端,此時server的狀態變為FIN_WAIT_1。client的接收FIN後變為CLOSING,同時server也變為CLOSING。
3. client發送ACK+接收到的sequence+1給server。client的狀態變為TIME_WAIT。
4. server同時也發送ACK+接收到的seqnce+1給client。此時client和server都變為CLOSED。
整個過程是這麼一個過程,那麼為什麼TCP連接的時候需要三次,而關閉的時候需要四次?
因為建立連接的時候SYN+ACK可以同時發送,不影響連接建立。而關閉的時候FIN+ACK不能合起來,因為TCP是雙向且全雙工連接。也就是client和server建立好連接後,client和server即能發送資訊同時也能接收資訊。當client發送FIN給server的時候,只能說明客戶端不給server發送數據了,但是不證明client不接收數據,所以給到server後,server處理好之後說我也不給你發數據了(FIN)。然後我已經你不給我發數據了(ACK),這個時候client收到後說知道了(ACK), 此時連接就關閉了。
好了,關於這篇就整理到這裡,如果有不對的地方歡迎批評指正。