文章目錄
- 一、認識一些網絡基礎概念
- 1.1、ip地址
- 1.2、端口號
- 1.3、協議
- 1.4、協議分層
- 1.5、協議分層的2種方式
- 1.5.1、OSI七層模型
- 1.5.2、TCP/IP五層模型[!]
- 1.5.2.1、TCP/IP五層協議各層的含義及功能
- 二、網絡中數據傳輸的基本流程——封裝、分用
- 2.1、封裝
- 2.2、分用
- 2.2.1、5元組
- 三、進行網絡編程
- 3.1、UDP
- 3.1.1、UDP 的特點
- 3.2、TCP
- 3.2.1、TCP 的特點
- 3.3 TCP、UDP特點解析
- 3.4、使用傳輸層協議 TCP/UDP 提供的 socket api 進行網絡通信
- 3.4.1、UDP—— DatagramSocket、DatagramPacket
- 3.4.1.1 使用UDP協議編寫回顯服務器
一、認識一些網絡基礎概念
1.1、ip地址
使用 ip 地址,來描述網絡上一個設備所在的位置。
1.2、端口號
區分一個主機上不同的應用程序。
一個網絡程序,在啟動時,都需要綁定一個或多個端口號,因為后續的通信都需要依賴端口來展開。
1.3、協議
協議描述了網絡通信時傳輸的數據的含義。
1.4、協議分層
協議就只是表示一種約定,這樣的約定可以是任意的,由于網絡通信協議是復雜的,因此在經過了多年的發展后,行業標準、專家已經規定出了現成的協議,我們只需要學習好規定出的現成協議即可。
那為什么要約定好這么一份通用的協議呢?
這是由于在網絡通信中,電腦種類很多,制作設備的廠商也很多,必須有一份統一的協議標準,讓大家都按照同樣的標準研發設備,以確保不同的廠商研發出的東西都能夠在一起相互通信。
但由于網絡通信確實是十分復雜的,故會涉及到一系列非常繁瑣、細節的工作,因此僅靠一個協議就解決所有問題,就導致這個協議就會十分龐大、復雜,因此此時就需要對協議進行分層。
上層協議 調用 下層協議,下層協議 給 上層協議提供服務。
例子:
協議也是如此。
1.5、協議分層的2種方式
1.5.1、OSI七層模型
OSI七層模型一般只存在于教科書中。
1.5.2、TCP/IP五層模型[!]
TCP/IP五層模型是如今網絡通信最常用的模型,很重要!
1.5.2.1、TCP/IP五層協議各層的含義及功能
第一層:物理層
含義: 負責在物理媒介上發送和接收原始比特流。
功能: 為網絡通信提供物理連接,定義了如網卡、網口、網線等物理設備的標準。
第二層:數據鏈路層
含義: 為相鄰節點提供端到端的直接鏈接服務,以幀的形式傳輸數據。
功能: 錯誤檢測和修正,管理幀數據。
第三層:網絡層
含義: 負責數據在網絡中的路由選擇和轉發,確保數據從源 ip 地址傳送到 目的 ip 地址。
功能: 路由選擇,數據包的封裝與分用。
第四層:傳輸層
含義:直接提供端到端的服務,只關注起點和終點,不關注中間的實現過程。
功能: 對端口號進行管理
第五層:應用層
含義: 為應用程序提供網絡通信服務。
功能: 允許應用程序與網絡交互,實現數據傳輸、文件共享、電子郵件…等網絡通信功能。
舉個例子加深對TCP/IP五層模型中各層的理解:
1、物理層 :約定網絡通信中的一些基礎設施需要遵守的規范,如約定這些信息:網線、網口、網口… 就像快遞車行駛在公路上運送快遞時,公路、信號燈、公路上的綠化形式…都有一定的制定標準。
2、數據鏈路層 :相鄰節點之間,數據是如何傳輸的。快遞公司運送快遞時,確定好快遞的運輸路線之后,假設此時快遞車運送快遞的路線是:廣東——>柳州——>南寧,那么此時就需要考慮該條路線中的相鄰節點之間是怎么走的,如從廣東——>柳州,快遞是怎么運送的,到底是水運、還是航運…,而從柳州——>南寧,快遞是怎么運送的,到底是陸運、還是航運…
3、網絡層 :與路徑規劃有關。j就像當前快遞車運送快遞的路線是怎么規劃的:俗話說,條條大路通羅馬,因此運送快遞到達目的地其實可以有很多條不同的路線能夠到達目的地。譬如說有,廣東——>柳州——>南寧、廣東——>桂林——>南寧…究竟選擇哪條路線運送快遞,由網絡層負責。
4、傳輸層:只關注起點和終點,不關注中間過程。譬如網購時,需要告知賣家自己的收件地址,那此時賣家去快遞公司寄快遞給用戶時,就會告知快遞公司當前所寄商品是從哪里寄出,要寄往哪里,此時就行了,至于快遞公司將此快遞從哪里運輸到哪里,怎么打包、運輸、運輸的過程產生的油費、運輸的路徑規劃…都不需要我們關心,是由快遞公司負責,我們只需要在規定時間內能收到自己的快遞即可。
5、應用層:拿到這個數據之后我們要干什么。即獲取到數據之后是要進行文件傳輸、郵件發送、還是…其他的,是由應用層說了算的。
對于我們程序員來說,我們和傳輸層、應用層打的交道比較多,因此必須重點掌握傳輸層、應用層的含義、功能、api、使用。
二、網絡中數據傳輸的基本流程——封裝、分用
2.1、封裝
以QQ發送消息為例子:主機A發送消息給主機B,介紹網絡中傳輸數據的基本流程。
主機A的情況:
1、應用層:
QQ應用程序,從輸入框中獲取到我們要傳送的數據,根據應用層協議,構造成應用層數據報,應用層含有很多現成的協議,但很多應用程序中會自定義應用層協議,那么QQ自定義的應用層協議是啥樣的?咱們不知道,只有開發者才知道,因此此處QQ自定義的應用層協議我們通過假設進行舉例子。
假設QQ的應用層協議是這樣自定義的:
發件人的QQ號、接收人的QQ號、時間、消息內容
那么此時就需要將自定義好的應用層協議,構造成應用層數據報,構造應用層數據報的過程,就是按照一定格式進行字符串拼接。
發送方和接收方需要達成一致,發送方使用什么樣的應用層協議進行發送數據,接受方就要使用什么樣的應用層協議接收數據。
應用程序自定義的好應用層協議后,并且構造好應用層數據報后,就會調用傳輸層提供的接口,把應用層數據報(攜帶了應用程序所想發送的消息)交給傳輸層進行處理。
2、傳輸層:
傳輸層的現成協議很多,最常用的就是 TCP 協議、UDP 協議,此處假設使用 UDP 協議處理應用層傳過來的的數據報。
UDP 協議就會按照自己的協議格式,生成一個 UDP 數據報:該 UDP 數據報,會將應用層數據報作為自己的載荷部分,并且在載荷部分的前面添加一個 UDP 報頭。
UDP 協議不關心應用層數據報里含有什么數據,是什么內容,只是把應用層數據報當作一個字符串,再構造出一個傳輸層數據報。
就像發快遞一樣,假設你想寄的快遞是衣服,你拿到快遞站之后,交給快遞員,快遞員會把你的衣服包裝成一個包裹,然后在上面貼上快遞單,此時你要寄的衣服(相當于應用層數據報)就已經變了一個樣子,變成了一個快遞包裹件,上面有快遞單(此時這個貼了快遞單的包裹,就相當于 UDP 數據報)。只要你郵寄的東西,不管是衣服、還是其他的,只要不違禁,快遞是不會關心你要寄什么東西的,他只負責給你包裝,然后貼單子(這個單子上面就含有快遞的發件人、收件人…這里的發件人、收件人就相當于一個 源端口、目的端口),發出去。
3、網絡層:
網絡層中也含有眾多現成的網絡層協議,最主要的協議的是 IP 協議。 IP協議會根據從傳輸層中收到的 UDP 數據報,構造出 IP 數據報,IP數報中就包含 源IP地址 和 目的IP地址,來確保數據能夠從 源IP地址 正確傳輸到 目的IP 地址。
就像我們發快遞一樣,在快遞上貼的快遞單上的發件人地址和收件人地址,就相當于 源IP地址 和 目的IP地址,都是為了確保正確傳輸。
4、數據鏈路層:
數據鏈路層也提供了許多現成的協議,最主要的是 以太網 協議,以太網,又會針對網絡層傳輸來的數據報進行封裝,為IP數據報添上幀頭、幀尾。
以太網也不關心載荷里是啥,只是把載荷當作字符串,進一步的拼接上幀頭、幀尾,構造成以太網數據幀,然后進一步再將以數據鏈路層數據報發送給物理層。
5、物理層:
物理層是硬件設備(如網卡),硬件設備需要將上述數據進行轉換,將拼接好的字符串數據轉成二進制數據,通過光信號/電信號/電磁波傳輸。
此時,將數據進行了多層封裝,最終將信號轉成了光信號,在網絡中進行傳輸,主機A就完成了發送過程。
2.2、分用
主機B的情況。
1、物理層:
硬件設備(網卡),收到網絡方傳輸來的光信號/電信號/電磁波,那此時就需要通過調制解調器(貓),針對光信號進行調制解調。
調制:把你要傳輸的信息放到光電信號中。
解調:從光電信號中把信息取出來。
那此時,物理層就通過調制解調器器,將光電信號中的數據取出來,即:以太網的數據幀,這個數據就要被交給上一層,數據鏈路層。
2、數據鏈路層
數據鏈路層的以太網協議,就會針對這個數據進行解析,即:將以太網數據幀的幀頭、幀尾去掉,取出其載荷部分,交給上層——網絡層。
3、網絡層:
IP協議針對這個數據進行解析:即去掉IP報頭,取出載荷,進一步交給傳輸層。
4、傳輸層:
根據IP報頭中的字段,就知道當前這個載荷是一個 UDP 數據報,故將此數據報交給 UDP 協議處理,此時UDP也要針對數據報進行解析,去掉報頭。
5、應用層:
UDP報頭中,有一個字段——>目的端口,根據目的端口找到關聯的應用程序,然后將此應用層數據報交給這個程序即可,該程序會根據自定義的應用層協議解析該數據報,然后將數據顯示到界面上。
此時,完成上述一系列分用步驟后,QQ中對應的頭像就開始閃爍,點進去,就能顯示出這個消息,以及消息的接收時間等信息…
主機A,從上到下,依次添加報頭的過程,稱為 “封裝”,主機B,從下到上,依次解析報頭的過程,稱為 “分用”,每次網絡傳輸,都需要經歷這個過程。封裝就像是在打包快遞,分用,就像是在拆快遞。
消息轉發到某個設備,每個設備的處理流程都是和上面的封裝分用是一致的。
如果消息轉發到的設備是一個交換機,交換機封裝分用到數據鏈路層即可:交換機解析出以太網數據幀,進一步獲取到幀頭中的 “mac”地址,根據 "mac"地址查詢交換機內部的轉發表,確定接下來數據從哪個網口發出去,在發送之前又會把以太網數據幀封裝好。
如果消息轉發到的設備是一個路由器,路由器封裝分用到網絡層即可:路由器解析出IP數據報,進一步獲取到IP報頭中的I目的P地址,根據目的IP地址進一步規劃接下來要走的路線,然后在發送之前又會把IP數據報封裝好。
2.2.1、5元組
使用 5元組 來描述一次網絡通信:1、源 IP 地址 2、目的 IP 地址 3、源端口號 4、目的端口號。5、協議類型。
三、進行網絡編程
寫一個應用程序,這個應用程序可以使用網絡通信,就需要依靠操作系統給傳輸層協議對外提供的api。傳輸層主要的協議有:TCP、UDP,這兩個協議關于網絡通信方面,提供了兩套完全不同的 api。
傳輸層用于網絡通信的 api 叫做 socket api。
3.1、UDP
3.1.1、UDP 的特點
1、無連接
2、不可靠傳輸
3、面向數據報
4、半雙工
3.2、TCP
3.2.1、TCP 的特點
1、有連接
2、可靠傳輸
3、面向字節流
4、全雙工
3.3 TCP、UDP特點解析
1、什么叫做 無連接?有連接?
譬如說我們在javase時學過的JDBC編程,JDBC是:先創建一個數據源 DataSource,再通過 DataSource 創建 Connection 連接,那么這就是一種 有連接,該連接是抽象的,連接是用來通信時保存對方信息的。
再譬如說,打電話。當我們打電話時,首先按下撥號鍵,直到對方接通為止,才算是完成連接,這也是一種有連接的表現。
那么客戶端與服務器之間進行通信時,使用 內存(本本)保存對端的信息,雙方都保存這個信息,此時 “連接” 就出現了,那么與JDBC的連接、打電話的連接不同的是,一個客戶端可以連接多個服務器,一個服務器也可以對應多個客戶端。
那么像QQ、微信進行發送消息時,是不需要建立連接的,就能直接發送消息進行通信,這是一種 無連接 的變現。
其實除了連接,還有一種叫做 鏈接 的情況,鏈接與連接不同,鏈接相當于一種快捷方式,即:通過一個文件,讓該文件的內容保存另一個文件的路徑,實現的鏈接,一般是軟鏈接(軟件的鏈接)。
2、什么叫 可靠傳輸?什么叫 不可靠傳輸?
可靠傳輸,不是說A給B發消息,100%能到,這個要求太難了,可靠傳輸,就是說,A給B發消息時,會盡可能地將消息傳給B,并且傳輸失敗的時候,A能感知到、或者在傳輸的時候,A能知道自己是否傳輸成功!
譬如說,對于應用程序:釘釘、飛書、企業微信…這些應用程序用到的就是可靠傳輸,即當用戶A給用戶B發消息時,用戶A能夠清楚地感知到用戶B是否接收到消息,如果消息旁邊出現 “已讀”,那就說明用戶B已經收到該消息并且查看了;如果消息旁邊什么都沒有出現,說明當前消息已經通過網絡傳輸到給了用戶B,但是用戶B并沒有點擊查看該消息。
不可靠傳輸即:就像我們平常所用的QQ、微信…這些軟件,當用戶A給用戶B發送了一條消息,無論用戶是否點擊查看了該條消息,該消息的是輸入框旁邊,都不會出現 “已讀” 的提示,因此,用戶A此時就無法判斷當前消息的發送情況。
TCP的特點就是可靠傳輸,但是可靠傳輸的前提是TCP犧牲了傳輸效率,UDP的特點是不可靠傳輸,因此UDP的傳輸效率比TCP高,但是UDP并不能保證消息傳輸過程的可靠性,極易出現丟包、順序顛倒、數據包重復的情況。
3、什么叫做 面向數據報?面向字節流?
面向數據報、面向字節流 這樣的特點是跟代碼的編寫息息相關的,務必要重視的去掌握該知識點。對于 UDP 來說,其在網絡中傳輸數據的基本單位是 數據報,那么一個UDP數據報的格式,是十分重要的,后續會介紹。而TCP在網絡中傳輸數據的基本單位是 字節,就跟文件操作類似,都是 “流” 式的。譬如說,通過 TCP 讀寫 100字節數據,可以一次讀寫 100 字節,也可以分2次讀寫,分別讀寫50字節;也可以分10次讀寫,每次讀寫10字節。
[易錯題]:TCP是可靠傳輸、UDP是不可靠傳輸,因此TCP比UDP更安全?這是正確的還是錯誤的?[錯誤的],談到網絡安全,指的是:你傳輸的數據是否容易被黑客截獲,以及如果被截獲之后是否會泄露一些重要信息,網絡安全是和 安全、入侵、加密、反編譯…有關,而不是和可靠傳輸與否有關。
4、什么叫做 全雙工?半雙工?、
全雙工:進行網絡通信的通道可以雙向傳遞數據。
半雙工:進行網絡通信的通道只能單向傳遞數據。
3.4、使用傳輸層協議 TCP/UDP 提供的 socket api 進行網絡通信
3.4.1、UDP—— DatagramSocket、DatagramPacket
UDP提供兩個不同的核心類進行網絡通信:
一、DatagramSocket:
操作系統是使用 文件 這樣的概念,來管理一些軟硬件資源,我們使用一些硬件的網絡設備進行網絡通信時(網卡…),操作系統也是使用 文件 這樣的方式管理網卡這樣的硬件設備,而我們將表示網卡的這類文件,稱為 socket 文件。
Java中的 socket 對象,就對應系統里的 socket 文件,最終落到網卡這樣用于進行網絡通信的硬件設備。
還有一些其他的硬件設備,譬如說:鍵盤、鼠標、顯示器等硬件設備插入電腦上使用時,操作系統也是將這些硬件設備當作 一個一個的 文件 管理起來。
因此,如果我們程序員寫的應用程序,想要進行網絡通信,就必須在代碼中先有 socket 對象。
那么,上述的 DatagramSocket 就是一個 socket 對象。
DatagramSocket 的構造方法:
1、DatagramSocket()
此構造方法是在客戶端處使用,客戶端想要進行網絡通信,也是需要先有一個 socket 對象。客戶端使用哪個端口,不需要客戶端自己指定,而是由系統自動分配,這是因為客戶端大部分是計算機小白,他們自己指定端口號,極易發生端口沖突,造成網絡通信失敗。
2、DatagramSocket(int port)
此構造方法是在服務器端使用的,服務器端需要自己指定端口號,以便客戶端能夠找到當前服務器來進行網絡通信。那為什么服務器端又能夠進行手動指定端口號??此時不怕端口沖突了??這是因為對于服務器端來說,服務器端是由程序員掌控的,因此此時程序員對于服務器上的應用程序的端口號分配情況了如指掌,因此此時指定的端口號是經過考察才分配的,此時就不會造成端口沖突,以至于網絡通信失敗這樣的情況發生了。
當客戶端給服務器發送消息時,對于客戶端來說,客戶端的端口號是源端口號,服務器的端口號是目的端口號;當服務器給客戶端發消息時,對于服務器來說,服務器的端口號是源端口號,客戶端的端口號是目的端口號。
舉個例子理解端口號在網絡通信中的重要作用:
有一天,我去城中街道58號餐廳吃飯。那么這個地址:城中街道58號 就唯一標識了一條街道上的某一家餐廳,因為一個城市,會有許多條街道,一條街道上,會有許許多多的餐館(那么對于一個服務器端來說,一個服務器上,也會有許多應用程序)。此時的 城中街道 就相當于服務器的 IP地址,58號 就相當于 服務器的端口號,而慕名而來吃飯的客人,就相當于客戶端,假如我第一次到這個餐廳里,坐的位置是10號桌,那么下一次來這個餐廳,坐的還是10號桌嗎??不一定了,這個位置是隨機分配的。因此此時 10號 桌 就相當于 客戶端的端口號,不需要客戶端自己指定,而是由系統自動分配即可。
3、DatagramSocket 中提供的用于網絡通信的方法:
1)、void receive(DatagramPacket p)
使用該方法讀取請求并解析,該方法的參數是一個 數據報,**因為 UDP 協議在網絡傳輸數據的基本單位的 數據報。
2)、void send( DatagramPacket p)
使用該方法發送一個數據報,該方法的參數也是一個 數據報。
3)、void close()
通過該方法,關閉 socket 文件,防止出現 文件資源泄露 問題。
二、DatagramPacket
DatagramPacket 表示一個 數據報。
DatagramPacket 的構造方法:
1、DatagramPacket(byte[] buf,int length)
該構造方法代表了系統中設定的 UDP 數據報的二進制結構。該構造方法使用 byte數組來接受數據,因為 DatagramPacket 作為 UDP 數據報,必然要能夠承載一些數據,我們需要通過手動指定 byte[] 作為數據存儲的空間。
2、DatagramPacket(byte[] buf,intt length,SocketAddress address)
該構造方法一般用于發送數據,參數有 byte 數組,用來為發送的數據作承載空間,length表示數據的實際長度,address表示IP地址以及端口號。
3.4.1.1 使用UDP協議編寫回顯服務器
回顯服務器:客戶端發什么、服務器就返回什么。
服務器端代碼:
package UDP.network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;//回顯服務器
public class UDPEchoServer {private DatagramSocket socket = null;ExecutorService executorService = null;
// 參數是:服務器的端口號,客戶端通過這個服務器的端口號與服務器進行網絡通信public UDPEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}// 啟動服務器public void start() throws IOException {
// 通過 while 循環反復處理 不知道何時與服務器進行通信的 眾多客戶端System.out.println("[UDPEchoServer] 服務器啟動!");while (true){// 服務器做的3步:
// 1、讀取請求并解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);try {socket.receive(requestPacket);} catch (IOException e) {e.printStackTrace();}
// 通過 getData() 獲取數據報中的數據String request = new String(requestPacket.getData(),0,requestPacket.getLength());
// 2、根據請求計算出響應 通過 process() 方法,完成根據請求計算出響應,這一步是最復雜最關鍵的,不同的服務器不同的功能,就是這一步不同String response = process(request);
// 3、返回響應 requestPacket.getSocketAddress():通過這個獲取到客戶端的ip地址以及端口號,這樣就可以將響應返回給特定的客戶端DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());try {socket.send(responsePacket);} catch (IOException e) {e.printStackTrace();}
// 打印日志信息System.out.printf("[%s:%d] req:%s,resp:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UDPEchoServer server = new UDPEchoServer(8909);server.start();}
}
客戶端代碼:
package UDP.network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;//客戶端
public class UDPEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;// 服務器的ip地址,端口號public UDPEchoClient(String ip,int port) throws SocketException {serverIp = ip;serverPort = port;socket = new DatagramSocket();}public void start() throws IOException {System.out.println("[UDPEchoClient] 客戶端啟動!");while (true){
// 從控制臺輸入用戶信息System.out.println("請輸入信息:-> ");Scanner sc = new Scanner(System.in);String request = sc.next();// 再將用戶輸入的信息構造成一個UDP數據報,發送給服務器處理DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp),serverPort);
// 發送請求socket.send(requestPacket);// 讀取服務器的響應,并解析出響應內容DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UDPEchoClient client = new UDPEchoClient("127.0.0.1",8909);
// UDPEchoClient client = new UDPEchoClient("42.192.83.143",9090);client.start();}
}
端口號,是用來區分主機上不同的應用程序,**一個應用程序可以占據主機上的多個端口號,一個端口只能被一個進程占有(**其實是有特例的,但是一般情況下是在這樣的)。如果應用程序在網絡通信中出現通信異常:SocketException ,一般都是因為當前端口號已經被別的進程占用,此時再嘗試創建這個 socket 對象,占用該端口,就會報錯——編寫網絡通信代碼時,此類異常會很常見!
解析使用UDP編寫回顯服務器代碼:
在服務器端的 start() 方法中,使用 while循環來等待并循環處理客戶端的請求,因為一個服務器需要給很多的客戶端提供服務,服務器不知道客戶端什么時候來,服務器只能 “時刻準備著”處理客戶端的請求。
而 socket.receive() 方法中,參數是DatagramPacket,是一個 “輸出型參數”,傳入 receive 的是一個空的 DatagramPacket 對象,receive() 內部會將這個空的 DatagramPacket 對象的內容填充上,當 receive() 執行結束之后,就會得到一個 裝滿內容的 DatagramPacket。
對于 response.getBytes().length 和 response.length()來說,這兩者有什么區別??這很重要,一定不要弄錯。如果當 response 里都是英文字母時,這兩者獲取到的長度是一樣的,但是如果說 response 里是2個漢字時,此時這兩者獲取到的長度就會有差別,一個漢字是3個字節,response.length()此時獲取到的長度是2,而 response.getBytes().length 獲取到的長度是 6。 Socket api 本身,是按照字節來處理數據的。
對于 requestPacket.getSocketAddress() 來說,是用來獲取IP地址和端口號的,DatagramPacket 這個對象里就包含了通信雙方的 IP地址和端口號。
socket對象 就相當于一個 socket 文件,因此 socket 對象用完后,就需要及時關閉,以免造成文件資源泄露問題,導致程序異常。那么此時 socket = new DatagramSocket(port); 來說,是否需要關閉呢??
對于我們當前編寫的這個服務器程序來說,DatagramSocket 不關閉,也是可以的。因為整個程序中,只有這么一個 socket 對象,且不是在頻繁創建的情況下。這個 socket 對象生命周期十分長,伴隨整個程序,此時,socket 就需要保持打開的狀態。
socket 對象 ——>系統中的 socket 文件——>文件描述符(文件描述符即表示當前進程所占有的文件,進程每打開一個文件,就會生成一個文件描述符)(關閉 socket 對象的主要原因,就是因為要釋放文件描述符),但是因為我們此時程序中只有一個 socket 對象,且生命周期伴隨整個程序,此時無需手動關閉,只需要等待進程結束后,系統將PCB回收后,PCB里的文件描述符表字段也就被銷毀了。
僅限于,只有一個 socket 對象,并且生命周期是跟隨整個進程。如果是含有多個 socket 對象,并且 socket 對象的生命周期短,需要頻繁創建、釋放,就必須去關閉 socket!
…