【Socket】TCP協議、UDP協議的Java入門示例

TCP(Transmission Control Protocol)傳輸控制協議

面向連接,可靠的基於字節流的傳輸通信協議,每次通信雙方都需要提前建立連接,因此需要三次握手(以下情景均以客戶端爲數據發出方,服務端爲數據接收方)
第一次【客戶端進入SYN_SEND狀態】
客戶端向服務端發送syn包,然後等待服務端回話。
李四:“喂,是張三嗎?”

第二次【服務端進入SYN_RECV狀態】
服務端收到syn包後,服務端向客戶端發送syn+ack包,然後等待客戶端回話。
張三:“我是張三,您是李四?”

第三次【雙方都進入ESTABLISHED狀態】
客戶端收到syn+ack包,確認服務端收到了syn包,然後將ack包發送給服務端進行確認。
李四:“我是李四,我有些事情要說,……”

第三次結束後,客戶端即開始向服務端傳輸數據,直到傳輸完畢後,進行四次揮手結束這個過程。

第一次【客戶端向服務端發起請求:我要掛斷連接了】
客戶端向服務端發送FIN=1的請求,要求斷開。
李四:“那就說到這裏,我忙去了”

第二次【服務端收到客戶端的請求,回覆:客戶端可以掛斷連接】
服務端向客戶端發送ACK=1進行確認。
張三:“去吧,這件事我知道了”

第三次【服務端首先斷開與客戶端的連接,回覆:服務端已經準備好】
服務端向客戶端發送FIN=1的請求,準備好斷開連接了。
張三:“拜拜”

第四次【客戶端收到服務端的請求,回覆:收到】
客戶端向服務端發送ACK=1的請求,然後斷開鏈接,服務端收到後也斷開了連接
李四:“拜拜”(掛斷)

TCP的Java示例

運行行時,先開啓服務端,再開啓客戶端
可以發送被序列化的對象,常用Map集合進行封裝,或者用Json或Base64轉碼的字符串
服務端前兩步驟通常放在一個主線程的循環中,沒有請求時,程序停在accept()方法,一旦接收到請求,後續創建線程,在線程中完成第三步開始的步驟

@Test
public void serverTCP() { 
     //服務端
    try {
        //1、創建通信對象,指定端口
        ServerSocket ss = new ServerSocket(8000);
        //2、監聽客戶端請求
        Socket s = ss.accept();//當前線程進入阻塞狀態,等待中
        //3、開啓輸入流,接收消息
        InputStream is = s.getInputStream();//發送方的信息都在s對象中
        //4、讀取接收到的信息
        ObjectInputStream ois = new ObjectInputStream(is);
        List<String> list = (List<String>) ois.readObject();
        for (String str : list) {
            System.out.print(str);//輸出
        }
        //5、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
        ois.close();
        is.close();
        s.close();
        ss.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

@Test
public void clientTCP() {
    //客戶端
    try {
        //1、創建通信對象,指定接收方的地址,IP和端口
        Socket s = new Socket("127.0.0.1", 8000);
        //2、開啓輸出流,發送請求
        OutputStream os = s.getOutputStream();
        //3、準備消息
        List<String> list = new ArrayList<>();
        list.add("TCP");
        list.add("測試");
        //4、發送消息
        ObjectOutputStream oo = new ObjectOutputStream(os);
        oo.writeObject(list);
        oo.flush();//刷新緩存,信息開始發送
        //5、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
        oo.close();
        os.close();
        s.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

UDP(User Datagram Protocol)用戶數據報協議

面向事務,適用於可靠性不高,延遲小的傳輸協議中,如常見的網絡直播,在線視頻,在線音頻,語音等,由於該協議是一種無連接的傳輸層協議,傳輸效率高,可靠性不好,網絡環境不是很好時容易出現中斷,如視頻暫停緩衝,語音模糊
發送方只關心誰來請求數據,然後將數據丟出去,至於數據是否完整無損的抵達接收方,不去判斷
至於發送方如何得到是誰需要請求數據,這些請求可以通過接收方的APP,通過TCP連接到服務端就可以很輕鬆的就可以得到了,然後發送方每隔一段時間更新發送的數據到接收方緩存,就可以完成在線直播了

相當於大學上課的情景,老師爲發送方,只管講課,醒着的同學聽到考試的重點,做了筆記,還有些睡着的同學什麼也沒聽到,老師當然不會走到那些學生面前叫醒然後重新強調一遍,老師會繼續講下去,一個又一個的知識點就講過去了

UDP的Java示例

運行時,先開啓接收方,再開啓發送方
【無論服務端還是客戶端,都可以是接收方和發送方,比如開直播:Up主(發送方)與B站(接收方),B站(發送方)與粉絲(接收方)】
由於接收方不知道發送方的數據報的信息,接收方創建數據報時,應該大於發送方的數據容量,或者雙方約定好固定的容量。

@Test
public void sendUDP() {
    //發送方
    try {
        //1、創建發送的信息
        List<String> list = new ArrayList<>();
        list.add("UDP");
        list.add("測試");
        //2、創建輸出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(list);
        //3、信息存入流中
        oos.flush();
        byte[] b = baos.toByteArray();
        if (b == null) return;//不能發送null
        //4、創建數據報,數據,長度,接收方的目標地址,端口
        DatagramPacket dp = new DatagramPacket(
                // b.length=73
                b, b.length, InetAddress.getByName("127.0.0.1"), 8000);
        //5、創建通信對象
        DatagramSocket ds = new DatagramSocket();
        //6、發送數據
        ds.send(dp);
        //7、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
        ds.close();
        oos.close();
        baos.close();
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Test
public void receiveUDP() {
    //接收方
    try {
        //1、創建通信對象,設置監聽端口
        DatagramSocket ds = new DatagramSocket(8000);
        //2、創建數據報
        byte[] bytes = new byte[1024];
        //最大和虛擬機配置有關,默認21_1917_2080,接收時,設置的長度不足會拋出EOF異常
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        //3、將接收到的數據寫入數據報
        ds.receive(dp);
        //4、創建流轉換爲對象
        ByteArrayInputStream bais = new ByteArrayInputStream(dp.getData());
        ObjectInputStream ois = new ObjectInputStream(bais);
        //5、獲取數據
        List<String> list = (List<String>) ois.readObject();
        for (String str : list) {
            System.out.print(str);//輸出
        }
        //6、關閉資源,嚴格按照先開後關原則關閉,順序不能改變
        ois.close();
        bais.close();
        ds.close();
    } catch (SocketException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章