⑴ 【計算機網路】Socket
socket進行通信的方式如下:
使用socket()系統調用能夠創建一個socket,它返回一個用來在後續系統調用中引用該socket的文件描述符。
socket存在於一個通信domain中,它確定:
現在操作系統支持下列domain:
每個socket實現都至少提供了兩種socket:流和數據報。這兩種類型在UNIX和Internet domain中都得到了支持。
流socket提供了一個可靠的雙向的位元組流通信信道:
數據報socket允許數據以數據報的形式進行交換。在使用時無需與另一個socket簡歷連接。
傳入bind()的addr比較復雜,每種socket domain都使用了不同的地址格式,如UNIX domain socket使用路徑名,而Internet domain socket 使用IP地址和埠號。struct sockaddr適用於所有domain,將各種domain特定的地址結構轉換成單個類型以供socket系統調用中的各個參數使用。
socket I/O 可以使用傳統的read()和write()系統調用或使用一組socket特有的系統調用send() recv() sendto() recvfrom()。默認情況下,這些系統調用在I/O操作無法被立即完成時阻塞,使用fcntl() F_SETFL 操作用啟用 O_NONBLOCK 打開文件狀態標記可以執行非阻塞I/O
listen()系統調用將文件描述符sockfd引用的流socket標記為被動,這個socket後面會被用來接受來自其他(主動的)socket的鏈接。
無法再一個已連接的socket(已成功執行connect()的socket或由accept()調用返回的socket)上執行 listen()
如果伺服器正忙於處理其他客戶端,那麼客戶端的connect()可能並不能馬上被accept(),這將產生一個未決的連接。
內核必須要記錄所有未決的連接請求的相關信息,backlog參數允許限制這種未決連接的數量。在這個限制之內的連接請求會立即成功,之外的連接請求就會阻塞直到一個未決的連接被接受,並從未決連接隊列中刪除。
accept()系統調用會文件描述符sockfd引用的監聽流socket上接受一個連入連接。如果在調用accept時不存在未決的連接,那麼調用會阻塞直到有連接請求到達為止。
返回的結果是已連接的socket的文件描述符。addr參數指向一個用來返回socket地址的結構。
一對連接的流 socket 在兩個端點之間提供了一個雙向通信信道。
關閉一個連接之後,對等應用程序讀取數據時將會收到文件結束(所有緩沖數據都讀取之後),如果要寫入數據,會收到一個SIGPIPE信號,並且系統調用返回EPIPE錯誤。
無法保證順序,也無法保證能夠到達。由於底層協議有時會重新傳包,也可能多次到達。
盡管數據報socket是無連接的,但在數據報socket上應用connect()系統調用仍然起作用,會導致內核記錄這個socket的對等socket地址。
當一個數據報socket已連接後:
在UNIX domain中,socket地址以路徑名來表示,domain特定的socket地址結構的定義如下:
為將一個UNIX domain socket綁定到一個地址上,需要初始化一個sockaddr_un結構,然後將指向這個結構的一個指針作為addr參數傳入bind()並將addrlen指定為這個結構的大小。
當用來綁定UNIX domain socket時,bind()會在文件系統中創建一個條目,作為socket路徑名的一部分的目錄需要可訪問和可寫。這個文件會被標記為一個socket,當再這個路徑名上應用stat()時,它會在stat結構的st_mode欄位中的文件類型部分返回值S_IFSOCK。
盡管UNIX domain socket是通過路徑名來標識的,但這些socket上發生的I/O無須對底層設備進行操作。
有關綁定一個UNIX domain socket的注意點:
伺服器流程:
客戶端流程:
對於UNIX domain socket來說,數據報的傳輸是在內核中發生的,也是可靠的,所有消息都會按序被遞送並且不會發生重復的狀況。
伺服器創建socket後並綁定後,進入一個無線循環,在循環中使用recvfrom()接收來自客戶端的數據報,將接收到的文本轉換成大小格式並使用通過recvfrom()獲取的地址將轉換過的文本返回給客戶端。
socket文件的所有權和許可權決定了哪些進程能夠與這個socket進行通信
有時候讓單個進程創建一對socket並將它們連接起來是比較有用的。
允許將一個UNIX domain socket綁定到一個名字上但不會在文件系統中創建的名字
要傳輸數據,數據鏈路層需要將網路層傳遞過來的數據報封裝進被稱為幀的一個一個單元。最大傳輸單元MTC是改層所能傳輸的幀大小的上限。
網路層任務:
網路層的協議是IP,IPv4使用32位地址來標識子網和主機,IPv6則使用了128位的地址。
一個裸socket(SOCK_RAW),允許程序直接與IP層進行通信,但大多數都會基於一種傳輸層協議之上的socket。
IP以數據報(包)的形式來傳輸數據。在兩個主機之間發送的每一個數據報都是在網路上獨立傳輸的,它們經過的路徑可能會不同。一個IP數據報包含一個頭,其大小范圍為20位元組到60位元組。包含目標主機的地址,源地址。
一個IP實現可能會給它所支持的數據報的大小設定一個上限。所有IP實現都必須做到數據報的大小上限至少與規定的IP最小重組緩沖區大小一樣大。IPv4限制值是576位元組,IPv6是1500位元組。
IP是一種無連接協議,並沒有在相互連接的兩個主機之間提供一個虛擬電路。
IP是一種不可靠的協議:盡最大可能將數據報從發送者傳輸給接收者,但並不保證包到達的順序與它們被傳輸的順序一致,也不保證是否重復,甚至到達。IP也美譽錯誤恢復。可靠性是通過使用TCP來保證的。
IPv4為IP頭提供了一個校驗和,這樣能夠檢測出頭中的錯誤,但並沒有為包中所傳輸的數據提供任何錯誤檢測機制。IPv6並沒有為IP頭提供校驗和,它依賴高層協議來完成錯誤檢測和可靠性。
IP數據報的重復使可能發生的,數據鏈路層採用一些技術確保可靠性以及IP數據報可能會以隧道形式穿越採用了重傳機制。
IP會將數據報分段成一個個大小合適的傳輸單元,這些分段在到達最終目的之後會被重組成原始的數據報(每個IP分段本身就包含一個偏移量)