聊聊UDP、TCP和實現一個簡單的JAVA UDP小Demo

黃青石發表於2020-05-24

  最近真的比較忙,很久就想寫了,可是一直苦於寫點什麼,今天腦袋靈光一閃,覺得自己再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), 此時連線就關閉了。

 

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

  

  

  

 

 

   

  

相關文章