深入学习理解UNIX网络编程
生活随笔
收集整理的這篇文章主要介紹了
深入学习理解UNIX网络编程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
深入學習理解UNIX網(wǎng)絡編程
- 一、OSI模型
- 二、傳輸層協(xié)議
- 三、傳輸控制協(xié)議(TCP)
- 四、TCP連接的建立和終止
- 1. 三路握手
- 2. TCP選項
- 3. TCP連接終止(4次揮手)
- 4. TCP狀態(tài)轉換圖
- 5. 觀察分組
- 五、TIME_WAIT 狀態(tài)
- 六、端口號
- 七、TCP端口號與并發(fā)服務器
- 八、緩沖區(qū)大小及限制
- 九、套接字地址結構
- 1. IPv4套接字地址結構
- 2. 通用套接字地址結構
- 3. 套接字地址結構的比較
- 十、socket函數(shù)
- 十一、connect函數(shù)
- 十二、bind函數(shù)
- 十三、listen函數(shù)
- 十四、accept函數(shù)
- 十五、fork和exec函數(shù)
- 十六、并發(fā)服務器
- 十七、close函數(shù)
- 十八、I/O模型
- 1. 阻塞式I/O模型
- 2. 非阻塞式I/O模型
- 3. I/O復用模型
- 4. 信號驅(qū)動式I/O模型
- 5. 異步I/O模型
- 6. 各種I/O模型的比較
- 十九、select函數(shù)
- 二十、poll 函數(shù)
一、OSI模型
- OSI(open systems interconnection):開放系統(tǒng)互連模型
- 七層模型:
- 1,2層:設備驅(qū)動程序和網(wǎng)絡硬件
- 網(wǎng)絡層
- 傳輸層:TCP和UDP(原始套接字:網(wǎng)絡應用可以繞過傳輸層直接使用網(wǎng)絡層的IP協(xié)議)
- 5,6,7:應用層(Web客戶瀏覽器、Telnet客戶、Web服務器、FTP服務器)
- 套接字編程接口:從應用層進入傳輸層的接口
- 接口在傳輸層的原因:分別處理應用細節(jié)和通信細節(jié)、分隔用戶進程和操作系統(tǒng)內(nèi)核
- 應用層(頂上三層):
- 處理具體網(wǎng)絡應用(FTP、Telnet、HTTP)的所有細節(jié),卻對通信細節(jié)了解很少
- 構成用戶進程
- 底下四層:
- 對具體網(wǎng)絡應用了解很少,卻處理所有通信細節(jié)(發(fā)送數(shù)據(jù)、等待確認、數(shù)據(jù)排序、計算驗證校驗和等等)
- 作為操作系統(tǒng)內(nèi)核的一部分
- 應用層(頂上三層):
二、傳輸層協(xié)議
- 傳輸層:TCP、UDP、SCTP(Stream Control Transmission Proto:流控制傳輸協(xié)議)
- UDP:簡單的、不可靠的、無連接、數(shù)據(jù)報協(xié)議
TCP:復雜的、可靠的、有連接、字節(jié)流協(xié)議(無消息邊界、流量控制、全雙工) - SCTP:可靠的、提供消息邊界、傳輸級別多宿支持
三、傳輸控制協(xié)議(TCP)
- 有連接:TCP提供客戶與服務器之間的連接,通過連接交換數(shù)據(jù)
- 可靠性:TCP向另一端發(fā)送數(shù)據(jù)時,要求對端返回一個確認,若沒有收到確認,TCP就自動重傳數(shù)據(jù)并等待更長的時間,數(shù)次重傳失敗后,TCP才放棄(4~10分鐘)
- 往返時間(RTT:round-trip time):客戶與服務器之間數(shù)據(jù)傳輸?shù)耐禃r間(TCP通過動態(tài)估算RTT來確定等待一個確認的時間)
- 排序:給每字節(jié)關聯(lián)一個序列號(可以判斷數(shù)據(jù)是否重復傳遞)
- 流量控制:TCP一次能夠從另一端接收多少字節(jié)(通告窗口)
- 通告窗口(通告形式告知滑動窗口大小):指出接收緩存區(qū)中當前可用的空間量
- 全雙工(full-duplex):在TCP連接的進出兩個方向可以既發(fā)送數(shù)據(jù)又接收數(shù)據(jù)
四、TCP連接的建立和終止
1. 三路握手
- 被動打開:服務器必須準備好接受外來的連接(通過調(diào)用socket、bind函數(shù))
- 主動打開:客戶端通過調(diào)用connect函數(shù)發(fā)起主動打開,導致客戶端發(fā)送一個SYN(同步)分節(jié),包含服務器將發(fā)送數(shù)據(jù)的初始序列號
- 服務器確認客戶的SYN并發(fā)送ACK(確認),同時自己發(fā)送一個SYN分節(jié)(服務器將在連接中發(fā)送數(shù)據(jù)的初始序列號)
- 客戶確認服務器的SYN
2. TCP選項
- 每一個SYN可以含有多個TCP選項
- TCP選項:
- MSS(maximun segment size):最大分節(jié)大小
- 窗口規(guī)模:告知對端的最大窗口大小(65535)
- 時間戳:防止失而復現(xiàn)的分組可能造成的數(shù)據(jù)損壞
3. TCP連接終止(4次揮手)
- 主動關閉:某個應用程序先調(diào)用close函數(shù),發(fā)送一個FIN分節(jié),表示數(shù)據(jù)發(fā)送完畢
- 被動關閉:接收到FIN的對端,將FIN作為一個文件結束符傳遞給自己的應用進程,并確認FIN
- 一段時間后,接收到這個文件結束符的應用進程調(diào)用close關閉socket,導致TCP發(fā)送一個FIN
- 接收到最終FIN的原發(fā)送端確認這個FIN,發(fā)送ACK
4. TCP狀態(tài)轉換圖
5. 觀察分組
五、TIME_WAIT 狀態(tài)
- TIME_WAIT狀態(tài):執(zhí)行主動關閉的一端
- 持續(xù)時間(2MSL):最大分節(jié)生命期(MSL)的兩倍,一般
- 分組在網(wǎng)絡中迷途:路由異常
- 發(fā)生原因:
- 某個路由器崩潰
- 某兩個路由器之間的某個鏈路斷開
- 導致:TCP超時重傳該分組,迷途的分組最終也到達,導致重復分組
- 發(fā)生原因:
- TIME_WAIT狀態(tài)存在理由:
- 可靠地實現(xiàn)TCP全雙工連接的終止:服務器沒有收到最終的ACK會再次發(fā)生FIN,TIME_WAIT狀態(tài)允許客戶端重新發(fā)送最終ACK
- 允許老的重復分節(jié)在網(wǎng)絡中消逝:TIME_WAIT狀態(tài)持續(xù)2MSL,使老的重復分組最多存活MSL秒即被丟棄,保證建立新的TCP連接時,來自該連接先前化身(同一個IP和端口)的老的重復分組已經(jīng)消逝
六、端口號
- Port number:端口號(16位整數(shù))
- 眾所周知的端口:0~1023(例如:80分配給Web服務器)
- 已登記的端口:1024~49151
- 臨時端口:49152~65535(動態(tài)的、私有的)
- 套接字對(socket pair):TCP連接兩端的四元組
- 本地IP地址
- 本地TCP端口號
- 外地IP地址
- 外地TCP端口號
- 一個套接字:IP地址 + 端口號
七、TCP端口號與并發(fā)服務器
- 服務器被動打開21端口,客戶使用記號 {:21,:*} 指出服務器的套接字對
- 監(jiān)聽套接字:“* . *”
- 21端口存在3個套接字
- 分節(jié)遞送給子進程1:來自206.168.112.219:1500,目的地為12.106.32.254:21
- 分節(jié)遞送給子進程2:來自206.168.112.219:1501,目的地為12.106.32.254:21
- 分節(jié)遞送給服務器主進程的監(jiān)聽套接字:目的端口號為21的其他TCP連接
八、緩沖區(qū)大小及限制
- MTU(maximun transmi unit):最大傳輸單元
- 路徑MTU:兩個主機之間的路徑最小的MTU
- IPv4數(shù)據(jù)報的最大大小是65535字節(jié),最小鏈路MTU68字節(jié)
- 以太網(wǎng)MTU:1500字節(jié)
- 分片:IP數(shù)據(jù)報的大小超過相應鏈路的MTU
- 主機和路由器都會分片
- 若設置了IP數(shù)據(jù)報首部的“不分片”位(DF位),即不允許被分片,若超過大小,則導致ICMP出錯消息
- 可利用DF位發(fā)現(xiàn)路徑MTU
- 最小重組緩沖區(qū)大小:IPv4的最小數(shù)據(jù)報大小(576字節(jié))
- MSS:TCP最大分節(jié)大小
- 重組緩沖區(qū)大小的實際值,避免分片(超過MTU將分片,MSS始終小于MTU)
- 經(jīng)常設置成MTU減去IP和TCP首部的固定長度(1500-20-20=1460字節(jié))
- TCP輸出:
- 某個應用進程調(diào)用write:操作系統(tǒng)內(nèi)核從應用進程的緩沖區(qū)中復制所有數(shù)據(jù)到套接字的發(fā)送緩沖區(qū)中
- 可通過SO_SNDBUF(套接字選項)更改TCP套接字發(fā)送緩沖區(qū)大小
- 若發(fā)送緩沖區(qū)無法容納數(shù)據(jù):應用進程將被置于休眠狀態(tài),內(nèi)核將不從write系統(tǒng)調(diào)用返回,直到所有數(shù)據(jù)都復制到發(fā)送緩沖區(qū)中
- TCP提取套接字發(fā)送緩沖區(qū)中的數(shù)據(jù),并把它發(fā)送給對端TCP
- 對端TCP必須確認(ACK)收到的數(shù)據(jù),本端TCP才能從發(fā)送緩沖區(qū)中丟棄已確認的數(shù)據(jù)
九、套接字地址結構
1. IPv4套接字地址結構
- IPv4套接字地址結構:網(wǎng)際套接字地址結構,以sockaddr_in命名
- 說明:地址結構大小至少16字節(jié)
- sin_len:長度字段
- sin_family、sin_addr、sin_port:POSIX規(guī)范需要的三個字段
- IP地址和端口號在套接字地址結構中以網(wǎng)絡字節(jié)序來存儲
- 結構本身不在主機之間傳遞
2. 通用套接字地址結構
- 套接字函數(shù):以指向某個通用套接字地址結構的一個指針作為參數(shù)之一
- 通用套接字地址結構的用途:對指向特定于協(xié)議的套接字地址結構的指針執(zhí)行類型強制轉換
3. 套接字地址結構的比較
- IPv4和Ipv6的套接字地址結構長度是固定的
- 長度是可變時,當把指向某個套接字地址結構的指針作為一個參數(shù)傳遞給某個套接字函數(shù)時,會把該結構的長度作為另一個參數(shù)傳遞給這個函數(shù)
十、socket函數(shù)
- 調(diào)用socket函數(shù),指定期望的通信協(xié)議類型
- family:協(xié)議族(常值)
- type:套接字類型(常值)
- protocol:某個協(xié)議類型常值或為0
- 成功時返回:小的非負整數(shù)(套接字描述符:sockfd)
- AF_XXX和PF_XXX對比:address family、protocol family(AF_:地址族、PF_:協(xié)議族)
十一、connect函數(shù)
- TCP客戶用 connect 函數(shù)建立與TCP服務器的連接:
- sockfd:socket函數(shù)返回的套接字描述符
- 第二、三個參數(shù):指向套接字地址結構的指針、該結構的大小
- 調(diào)用connect函數(shù)將激發(fā)TCP的三路握手過程,連接建立成功或出錯時返回:
- 出錯返回:
- ETIMEDOUT:TCP客戶未收到SYN分節(jié)的響應(多次發(fā)送后,未收到)
- ECONNREFUSED(硬錯誤):服務器對客戶SYN的響應是RST(復位),服務器主機在指定端口上沒有進程等待連接
- ICMP錯誤(軟錯誤):客戶發(fā)出的SYN在中間的某個路由器引發(fā)了目的對不可達的ICMP錯誤
- 套接字從CLOSED狀態(tài)轉移到SYN_SENT狀態(tài)
- 成功:轉移為ESTABLISHED狀態(tài)
- 失敗:該套接字不可用,必須關閉并重新調(diào)用socket
- 出錯返回:
十二、bind函數(shù)
- bind函數(shù)把一個本地協(xié)議地址賦予一個套接字:
- sockfd:socket函數(shù)返回的套接字描述符
- 第二、三個參數(shù):指向特定于協(xié)議的地址結構的指針、該結構的長度
- 返回錯誤:EADDRINUSE(地址已使用)
- 進程可以把一個特定的IP地址捆綁到它的套接字上
- TCP客戶:為在該套接字上發(fā)送的IP數(shù)據(jù)報指派了源IP地址
- TCP服務器:限定該套接字只接收那些目的地為這個IP地址的客戶連接
十三、listen函數(shù)
- listen函數(shù)由TCP服務器調(diào)用,在調(diào)用socket和bind函數(shù)之后,調(diào)用accept函數(shù)之前
- listen函數(shù)把一個未連接的套接字轉換成一個被動套接字(被動打開),指示內(nèi)核應接受指向該套接字的連接請求(套接字從CLOSED狀態(tài)轉換到LISTEN狀態(tài))
- 第二個參數(shù)(backlog):規(guī)定了內(nèi)核應該為相應套接字排隊的最大連接個數(shù)
- 內(nèi)核為監(jiān)聽套接字維護了兩個隊列:
- 未完成連接隊列:服務器正在等待完成TCP三路握手過程,套接字處于SYN_RCVD狀態(tài)
- 當客戶的SYN到達時,TCP在未完成連接隊列中創(chuàng)建一個新項
- 響應三路握手的第二個分節(jié)(服務器的SYN響應和對客戶SYN的ACK),該新項一直保留到客戶的ACK到達前,或該項超時
- 已完成連接隊列:已完成三路握手過程,套接字處于ESTABLISHED狀態(tài)
- 該項從未完成連接隊列移到已完成連接隊列的隊尾
- 該項從未完成連接隊列移到已完成連接隊列的隊尾
- 未完成連接隊列:服務器正在等待完成TCP三路握手過程,套接字處于SYN_RCVD狀態(tài)
- backlog參數(shù):
- backlog曾經(jīng)規(guī)定了兩個隊列總和的最大值
- Berkeley實現(xiàn)給backlog增設一個模糊因子(該因子乘以1.5得倒未處理隊列最大長度)
- 不要把backlog定義為0,(不同實現(xiàn)有不同解釋)
- 設定一個默認值,并允許通過命令行選項或環(huán)境變量覆蓋這個默認值
十四、accept函數(shù)
- accept函數(shù)由TCP服務器調(diào)用,用于已完成連接隊列隊頭返回下一個已完成連接,若隊列為空,則進程被置于休眠狀態(tài)
- 參數(shù):
- sockfd:監(jiān)聽套接字描述符(由socket創(chuàng)建,隨后用作bind和listen的第一個參數(shù)的描述符)
- cliaddr和addrlen(值-結果參數(shù))用來返回已連接的客戶協(xié)議地址
- accept返回值
- 成功:
- 由內(nèi)核自動生成的一個全新描述符(已連接套接字描述符),代表與所返回客戶的TCP連接
- 客戶進程的協(xié)議地址(cliaddr)以及該地址的大小(addrlen)(可將兩個設置為空指針,不返回協(xié)議地址)
- 失敗:出錯指示的整數(shù)
- 成功:
十五、fork和exec函數(shù)
- fork(派生)函數(shù):
- 調(diào)用一次,返回兩次:
- 調(diào)用進程(父進程)中,返回新派生進程(子進程)的進程ID號
- 子進程中返回0
- 子進程只有一個父進程,父進程可以有很多子進程
- 通過子進程調(diào)用getppid可以獲得父進程ID
- 無法通過父進程獲得子進程ID
- 調(diào)用accept后調(diào)用fork:已連接套接字在父進程和子進程之間共享
- 用法:
- 一個進程創(chuàng)建自身的副本(子進程):每個副本可以在另一個副本執(zhí)行其他任務的同時處理各自的操作(網(wǎng)絡服務器)
- 一個進程想要執(zhí)行另一個程序:進程創(chuàng)建自身的副本后,其中一個副本調(diào)用exec把自身替換成新的程序(shell程序)
- 調(diào)用一次,返回兩次:
- exec函數(shù)(6個):把當前進程映像(副本)替換成新的程序(進程ID不變)
- 調(diào)用進程:調(diào)用exec的進程
- 新程序:新執(zhí)行的程序
十六、并發(fā)服務器
- 并發(fā)服務器:同時服務多個客戶
- 編寫方法:fork一個子進程來服務客戶(通過已連接套接字描述符:connfd),父進程等待新客戶連接(通過監(jiān)聽套接字描述符:listenfd)
- 父進程對connfd調(diào)用close后,不會終止服務器與客戶的連接(引用計數(shù):2變?yōu)?,不等于0)
十七、close函數(shù)
- 關閉套接字,終止TCP連接,調(diào)用后發(fā)送一個FIN
- 并發(fā)服務器中:父進程close導致相應描述符的引用計數(shù)值減一,直到子進程close將計數(shù)值減為零時,才斷開TCP連接
十八、I/O模型
- I/O復用:
- 進程預先告知內(nèi)核的能力(使內(nèi)核一旦發(fā)現(xiàn)進程指定的一個或多個I/O條件就緒,就通知進程)
- 由select和poll兩個函數(shù)支持
- I/O復用使用場景:
- 客戶處理多個描述符(交互式輸入和網(wǎng)絡套接字)
- 一個客戶同時處理多個套接字
- 一個TCP服務器既處理監(jiān)聽套接字,又處理已連接套接字
- 一個服務器既處理TCP,又處理UDP
- 一個服務器處理多個服務或多個協(xié)議
- I/O模型
- 五種IO模型:阻塞式IO、非阻塞式IO、IO復用(select和poll)、信號驅(qū)動式IO、異步IO
- 一個輸入操作包括兩個階段:
- 等待數(shù)據(jù)準備好(等待數(shù)據(jù)從網(wǎng)絡中到達)
- 從內(nèi)核向進程復制數(shù)據(jù)(把數(shù)據(jù)從內(nèi)核緩沖區(qū)復制到應用進程緩沖區(qū))
1. 阻塞式I/O模型
- BIO:Blocking I/O(阻塞IO)
- 進程在從調(diào)用recvfrom(系統(tǒng)調(diào)用)開始到返回的整段時間內(nèi)是被阻塞的
2. 非阻塞式I/O模型
- 進程把一個套接字設置成非阻塞,是在通知內(nèi)核:當所請求的I/O操作非得把本進程置于休眠狀態(tài)才能完成時,拒絕把進程置于休眠,而是返回一個錯誤
- 通過輪詢(poll)內(nèi)核,循環(huán)調(diào)用recvfrom來完成非阻塞IO(耗費大量CPU時間)
3. I/O復用模型
- 可以調(diào)用select或poll,阻塞在這兩個系統(tǒng)調(diào)用中的某一個上,而不是阻塞在真正的I/O系統(tǒng)調(diào)用上
- 阻塞于select調(diào)用,等待數(shù)據(jù)報套接字變?yōu)榭勺x(select返回套接字可讀時,再調(diào)用recvfrom復制數(shù)據(jù))
4. 信號驅(qū)動式I/O模型
- 使用信號,讓內(nèi)核在描述符就緒時,發(fā)送SIGIO信號通知
- 需要先開啟套接字的信號驅(qū)動式I/O功能,再通過sigaction系統(tǒng)調(diào)用安裝一個信號處理函數(shù)(調(diào)用將立即返回),進程此時繼續(xù)執(zhí)行(非阻塞)
5. 異步I/O模型
- AIO(asynchronous IO):告知內(nèi)核啟動某個操作,并讓內(nèi)核在整個操作完成后通知我們
- 調(diào)用aio_read函數(shù),告訴內(nèi)核當整個操作完成時如何通知我們,該系統(tǒng)調(diào)用立即返回,且在等待IO完成期間,進程不被阻塞(下圖:要求內(nèi)核完成操作時產(chǎn)生某個信號)
6. 各種I/O模型的比較
- 前四種模型主要區(qū)別在第一階段,第二階段都是一樣的(數(shù)據(jù)從內(nèi)核復制到調(diào)用者的緩沖區(qū)期間,進程阻塞于recvfrom調(diào)用:同步I/O)
- 同步IO(前四種IO):導致請求進程阻塞,直到I/O操作完成
- 異步IO:請求進程不會被阻塞
十九、select函數(shù)
- 允許進程指示內(nèi)核等待多個事件中的任何一個發(fā)生,并只在有一個或多個事件發(fā)生或經(jīng)歷一段指定的時間后才喚醒它
- 例:調(diào)用select告知內(nèi)核對哪些描述符的哪些事件感興趣以及等待多長時間
- {1,4,5}中的任何描述符準備好讀
- {2,7}中的任何描述符準備好寫
- {1,4}中的任何描述符有異常條件待處理
- 經(jīng)歷10.2秒
- 參數(shù):
- timeout:告知內(nèi)核等待所指定描述符中的任何一個就緒可花多長時間
- 永遠等待下去:設置為空指針
- 等待一段固定時間:設置秒數(shù)和微秒數(shù)
- 根本不等待(輪詢:poll):檢查后立即返回,設置為0
- 中間三個參數(shù)readset、writeset、exceptset:指定要讓內(nèi)核測試讀、寫和異常條件的描述符(使用描述符集fd_set,整數(shù)數(shù)組)
- maxfdp1:指定待測試的描述符的個數(shù)(最大描述符+1:即 0,1,2,…,maxfdp1-1)
- timeout:告知內(nèi)核等待所指定描述符中的任何一個就緒可花多長時間
- 描述符就緒條件:
- 套接字準備好讀:讀操作不會阻塞
- 該套接字接收緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù)大于等于套接字接收緩沖區(qū)低水位標記的當前大小
- 該連接的讀半部關閉(接收了FIN的TCP連接)
- 該套接字是一個監(jiān)聽套接字且已完成的連接數(shù)不為0
- 其上有一個套接字錯誤待處理,讀操作返回錯誤(待處理錯誤)
- 套接字準備好寫:
- 該套接字發(fā)送緩沖區(qū)中的可用空間字節(jié)數(shù)大于等于套接字發(fā)送緩沖區(qū)低水位標記的當前大小
- 該連接的寫半部關閉
- 使用非阻塞式connect的套接字已建立連接(或者connect失敗了)
- 其上有一個套接字錯誤待處理
- 套接字有異常條件待處理:該套接字存在帶外數(shù)據(jù)或者仍處于帶外標記
- 套接字準備好讀:讀操作不會阻塞
- 低水位標記設置為64:至少存在64字節(jié)的數(shù)據(jù),否則應用進程不工作(防止少于64字節(jié)的數(shù)據(jù)準備好讀時select喚醒進程)
- select的最大描述符數(shù)
二十、poll 函數(shù)
- 與select類似,不過在處理流設備時,能夠提供額外的信息
- poll識別三類數(shù)據(jù):流的實現(xiàn)
- 普通:所有正規(guī)TCP數(shù)據(jù)和UDP數(shù)據(jù)、讀半部關閉時、監(jiān)聽套接字上有新的連接可用
- 優(yōu)先級帶:TCP的帶外數(shù)據(jù)
- 高優(yōu)先級
- 參數(shù):
- fdarray:指向一個結構數(shù)組第一個元素的指針(每個數(shù)組元素都是一個pollfd結構:用于指定測試某個給定描述符fd的條件)
- evenets:測試條件
- revents:返回該描述符的狀態(tài)
- nfds:結構數(shù)組中元素的個數(shù)
- timeout:指定poll函數(shù)返回前等待多長時間
- INFTIM(負值):永遠等待
- 0:立即返回,不阻塞進程
- 大于0:等待指定數(shù)目的毫秒數(shù)
- fdarray:指向一個結構數(shù)組第一個元素的指針(每個數(shù)組元素都是一個pollfd結構:用于指定測試某個給定描述符fd的條件)
- 返回:
- 就緒描述符的個數(shù)(revents成員值非0的描述符個數(shù))
- 0:定時器到時之前沒有任何描述符就緒
- -1:發(fā)生錯誤
總結
以上是生活随笔為你收集整理的深入学习理解UNIX网络编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java根据id查询名字_Mybatis
- 下一篇: 荷兰国旗排序问题