聊聊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), 此時連接就關閉了。

 

好了,關於這篇就整理到這裡,如果有不對的地方歡迎批評指正。

  

  

  

 

 

   

  

Tags: