應用層協議:應用層是面對程序員的一層,應用程序是程序員自己寫的,因此應用層協議由程序員自己決定。
1、自定製協議:
- 序列化:將多個數據對象按照指定的協議進行組織成爲持久化存儲/數據傳輸的二進制數據串。
- 反序列化:將二進制數據串通過指定協議進行解析得到各個數據對象。
- 序列化方式:結構體二進制序列化,json,protobuf.
2、知名協議:
- HTTP-應用層的超文本傳輸協議-html。
- 網址:URL-統一資源定位符-定位網絡中某臺主機上的某個資源。
- 如何定位:url包含的要素-協議方案名稱://用戶名:用戶密碼@服務器IP:服務器PORT/資源路徑?查詢字符串#片段標識符
- 協議方案名稱:確定本次請求使用什麼協議。
- 用戶名/密碼:本次訪問的客戶端認證信息(很少使用)。
- 服務器IP/PORT:定位網絡中的一臺主機上的處理進程。
- 資源路徑:請求服務器上的某個資源。
- 查詢字符串:客戶端提交給服務端的數據,由key=value鍵值對組成,鍵值對之間以&符號進行間隔。
- urlencode:url編碼,提交的數據中不能出現特殊字符,一旦包含就要進行轉義,將特殊字符每個字節轉換爲16進制數字字符,使用%標識。
- urldecode:url解碼,在查詢字符串中遇到%,將其第一個字符轉換爲數字乘以16加上第二個字符轉換後的數字。
HTTP協議實現
- 抓包工具:wireshark/fiddler.
- wireshark:網卡抓包工具,抓取流經網卡的所有數據流量-什麼包都能抓。
- fiddler:瀏覽器的代理工具,通過代理實現數據抓包-專業的http抓包工具。
- http/https:https是加密後的http協議,若要抓取https中的數據,則需要進行配置。
HTTP協議格式
請求:
首行:GET http://123.207.58.25/admin HTTP/1.1 以空格進行間隔包含三個要素,最終以\r\n作爲結尾。
請求方法:GET/POST/HEAD/PUT/DELETE/CONNECT/PATCH/OPTIONS/TRACE
- GET-請求實體資源-也可以通過url中查詢字符串向服務器提交數據-數據不安全/url長度有限制(各個服務器應用商的限制)
- POST-主要用於向服務器提交數據,提交的數據在正文中;GET請求沒有正文,POST請求有正文。
- HEAD-類似於GET,相較於GET,HEAD只響應頭部,而不響應正文。
URL: http://123.207.58.25/admin
協議版本:HTTP://1.1 0.9/1.0/1.1/2.0
- 0.9:僅用於傳輸html數據,並且只有GET請求方法且協議格式不完整。
- 1.0:正式規定了http協議格式,並且增加了多種請求方法且支持了不同文件格式的數據流。
- 1.1:在1.0的基礎上增加了更多請求方法和頭部描述信息且支持了長連接,管線化傳輸(響應的順序必須與請求的順序保持一致)。
- 2.0:採用二進制流傳輸,並且進行多路複用(響應順序可以不用與請求順序保持一致,頭部標識了請求信息)且允許服務端主動推送數據。
- 短連接:http基於在傳輸層tcp實現通信,建立連接,發送一個請求,得到響應後,則關閉連接。
- 長連接:一次連接可以發送多條請求。
頭部:描述本次請求的關鍵字段信息,由key:val形式的鍵值對組成,並且每個鍵值對以\r\rn作爲結尾 key:val\r\rnkey:val\r\rn
- Connection-控制長/短連接
- Cache-Control-緩存控制
- User-Agent-客戶端屬性
- Accept-描述自己所能接收的數據屬性
- Content-Length-描述正文長度
- Content-Type-描述正文數據類型
早期http是短連接通信,是一個無狀態協議,不會保存客戶端的狀態,每次訪問都需要進行登錄,因此引入Cookie保存客戶端狀態,Cookie-持續在通信中描述客戶端的通信狀態,但是不夠安全。
空行:間隔頭部與正文;接收http數據的時候,當連續收到兩個\r\n(\r\n\r\n)的時候,則認爲頭部到此結束。
先獲取完整頭部,通過頭部中的Content-Length獲取正文長度,然後獲取指定長度正文,通過這種方式每次獲取完整一條http請求數據。
正文:提交給服務端的數據。
響應:
首行:HTTP/1.1 303 See Other 包含三個要素,以空格進行間隔,以\r\n作爲結尾
協議版本:0.9/1.0/1.1/2.0
響應狀態碼:表示對本次的請求服務端所做出的響應結果
- 1xx:描述信息
- 2xx:表示本次請求正確處理完畢 典型 200
- 3xx:重定向-請求的資源在另一個位置,要求客戶端重新請求新位置;301-永久/302-臨時
- 4xx:表示客戶端請求錯誤;400-請求錯誤/404-表示請求的資源不存在
- 5xx:表示服務端錯誤;500-服務器內部錯誤 / 502-代理請求失敗,無效響應 / 504-代理請求超時
狀態碼描述:對狀態碼的描述信息,可以是官方文檔對應描述,也可以自定義。
頭部:關於本次響應的一些關鍵字段描述信息,以key:val鍵值對組成,以\r\n作爲結尾
- Transfer-Encoding:實體正文的傳輸方式
- Expires:緩存過期時間
- Location-3xx 重定向的新位置
- Set-Cookie-服務端通過set-cookie向客戶端傳遞信息,會被保存在客戶端瀏覽器的cookie文件中。
- Cookie-客戶端每次通信從cookie文件讀取數據通過cookie向服務端傳遞信息(維持客戶端狀態信息),cookie的使用不夠安全,因此使用Session搭配使用。
- Session-會話,服務端會爲每個登錄的客戶端創建會話,在服務端描述一些會話信息(客戶端身份信息,狀態信息),保存在服務端,可以通過set-cookie將session id 返回給客戶端,客戶端每次通信都會通過cookie帶有自己的sessio id;
- cookie和session區別:cookie持續傳遞客戶端狀態信息的字段,保存在客戶端,用於持續與服務端進行信息傳遞的一種手段。session是一種會話的控制,服務端保存的會話信息包含客戶端的身份狀態信息,通過cookie/set-cookie傳遞的session id 進行客戶端的身份狀態識別。
3. UDP協議
3.1 UDP協議端格式:UDP協議報頭只有8個字節
- 16位源端端口/16位目的端端口:描述端與端之間的通信;
- 16位UDP長度:表示整個數據報(udp首部+udp數據)的最大長度,即數據報最大大小位2^16byte=64kb;
- 16位校驗和:使用二進制反碼求和算法,校驗接收的數據與發送的數據是否一致;(二進制反碼求和算法->對報文從頭開始每個字節進行取反相加,高出16位則截斷高位,與低16位繼續相加,得到校驗和)
3.2 UDP特點
- 無連接:知道對端的IP和端口號就直接進行傳輸, 不需要建立連接;
- 不可靠:沒有確認機制, 沒有重傳機制;如果因爲網絡故障該段無法發到對方, UDP協議層也不會給應用層
返回任何錯誤信息;UDP不保證數據的可靠, 有序到達, 因此有可能亂序, 需要在應用層進行包序管理; - 面向數據報:應用層交給UDP多長的報文, UDP原樣發送, 既不會拆分, 也不會合並, 並且最大長度64KB;如果我們需要傳輸的數據超過64K, 就需要在應用層手動的分包, 多次發送, 並在接收端手動拼裝。
3.3 UDP的緩衝區
- UDP沒有真正意義上的 發送緩衝區. 調用sendto會直接交給內核, 由內核將數據傳給網絡層協議進行後續的傳輸動作;
- UDP具有接收緩衝區. 但是這個接收緩衝區不能保證收到的UDP報的順序和發送UDP報的順序一致; 如果緩衝區滿了, 再到達的UDP數據就會被丟棄;
- UDP的socket既能讀, 也能寫, 這個概念叫做 全雙工。
3.4 基於UDP的知名協議
- DHCP: 動態主機配置協議;
- DNS: 域名解析協議
4. TCP協議
4.1 TCP協議端格式
- 源/目的端口號: 表示數據是從哪個進程來, 到哪個進程去;
- 32位序號/32位確認號: 實現tcp的包序管理->tcp數據是有序交付的;
- 4位TCP報頭長度: 以4字節位單位描述tcp報頭長度,tcp報頭是不定長的,最小20字節, 最大15 * 4 = 60字節;
- 6位標誌位:
- URG: 緊急指針是否有效
- ACK: 確認號是否有效
- PSH: 提示接收端應用程序立刻從TCP緩衝區把數據讀走
- RST: 對方要求重新建立連接; 我們把攜帶RST標識的稱爲復位報文段
- SYN: 請求建立連接; 我們把攜帶SYN標識的稱爲同步報文段
- FIN: 通知對方, 本端要關閉了, 我們稱攜帶FIN標識的爲結束報文段
- 16位窗口大小: 實現滑動窗口機制,進行流量控制;
- 16位校驗和: 二進制反碼求和算法,校驗數據一致性;
- 16位緊急指針: 標識哪部分數據是緊急數據;
- 0~40字節選項數據:主要用於協商以及描述一些信息。
4.2 TCP數據傳輸格式
- TCP發送數據,是以字節流的形式發送。它不關心數據是什麼類型,但是爲了確保數據正確性,重發控制和重複控制等, 這些功能都以序列號來實現. 序列號初始值並非爲0,而是由客戶端建立連接時候,隨機產生的;
- TCP的數據長度並未寫入TCP首部。實際通信中求得TCP包的長度的計算公式是:IP首部中的數據包長度 – IP首部長度TCP首部長度;
- TCP將每個字節的數據都進行了編號. 即爲序列號. 每一個ACK都帶有對應的確認序列號, 意思是告訴發送者, 我已經收到了哪些數據; 下一次你從哪裏開始發。
4.3 確認應答機制
- 當數據從主機A發送給主機B時,主機B會返回給主機A一個確認應答。
4.4 超時傳輸機制
- 主機A發送數據給B之後, 可能因爲網絡擁堵等原因, 數據無法到達主機B; 如果主機A在一個特定時間間隔內沒有收到B發來的確認應答, 就會進行重發
- 主機A未收到B發來的確認應答, 也可能是因爲ACK丟失了,因此主機B會收到很多重複數據. 那麼TCP協議需要能夠識別出那些包是重複的包, 並且把重複的丟棄掉. 這時候我們可以利用前面提到的序列號, 就可以很容易做到去重的效果。
那麼, 如果超時的時間如何確定?
- 最理想的情況下, 找到一個最小的時間, 保證 “確認應答一定能在這個時間內返回”.
- 但是這個時間的長短, 隨着網絡環境的不同, 是有差異的.
- 如果超時時間設的太長, 會影響整體的重傳效率;
- 如果超時時間設的太短, 有可能會頻繁發送重複的包;
TCP爲了保證無論在任何環境下都能比較高性能的通信, 因此會動態計算這個最大超時時間.
- Linux中(BSD Unix和Windows也是如此), 超時以500ms爲一個單位進行控制, 每次判定超時重發的超時時間都是500ms的整數倍.
- 如果重發一次之後, 仍然得不到應答, 等待 2*500ms 後再進行重傳.
- 如果仍然得不到應答, 等待 4*500ms 進行重傳. 依次類推, 以指數形式遞增.
- 累計到一定的重傳次數, TCP認爲網絡或者對端主機出現異常, 強制關閉連接
4.5 TCP應答管理機制
服務端狀態轉化:
- [CLOSED -> LISTEN] 服務器端調用listen後進入LISTEN狀態, 等待客戶端連接;
- [LISTEN -> SYN_RCVD] 一旦監聽到連接請求(同步報文段), 就將該連接放入內核等待隊列中, 並向客戶端發送SYN確認報文.
- [SYN_RCVD -> ESTABLISHED] 服務端一旦收到客戶端的確認報文, 就進入ESTABLISHED狀態, 可以進行讀寫數據了.
- [ESTABLISHED -> CLOSE_WAIT] 當客戶端主動關閉連接(調用close), 服務器會收到結束報文段, 服務器返回確認報文段並進入CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 進入CLOSE_WAIT後說明服務器準備關閉連接(需要處理完之前的數據); 當服務器真正調用close關閉連接時, 會向客戶端發送FIN, 此時服務器進入LAST_ACK狀態, 等待最後一個ACK到來(這個ACK是客戶端確認收到了FIN)
- [LAST_ACK -> CLOSED] 服務器收到了對FIN的ACK, 徹底關閉連接
客戶端狀態轉化:
- [CLOSED -> SYN_SENT] 客戶端調用connect, 發送同步報文段;
- [SYN_SENT -> ESTABLISHED] connect調用成功, 則進入ESTABLISHED狀態, 開始讀寫數據;
- [ESTABLISHED -> FIN_WAIT_1] 客戶端主動調用close時, 向服務器發送結束報文段, 同時進入FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客戶端收到服務器對結束報文段的確認, 則進入FIN_WAIT_2, 開始等待服務器的結束報文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客戶端收到服務器發來的結束報文段, 進入TIME_WAIT, 併發出LAST_ACK;
- [TIME_WAIT -> CLOSED] 客戶端要等待一個2MSL(Max Segment Life, 報文最大生存時間)的時間, 纔會進入CLOSED狀態
爲什麼是三次握手
- 如果client發送的連接請求由於網絡延時到client連接釋放後纔到達server,這是早已失效的報文,如果只進行二次握手,服務器就認爲有新連接請求,但是client並沒有建立連接,不會給server發送數據,但是server爲了這個連接,會一直有資源消耗。
爲什麼是四次揮手
- 客戶端發送FIN包,表示客戶端不再發送數據,不表示客戶端不再接收數據,因此被動關閉方進行ACK回覆之後有可能還會繼續發送數據,等到不再發送數據,纔會發送下一個FIN包,因此FIN和ACK是分開的。
TIME_WAIT狀態有什麼用?爲什麼主動關閉方沒有直接進入closed釋放資源?
- 假設主動關閉方最後一次的ACK丟失,被動關閉方沒有收到最後一次ACK,超時後就會重傳一個FIN。
- 假設客戶端沒有TIME_WAIT而是直接釋放資源,就有可能啓動新的客戶端且使用與之前客戶端相同的地址信息。剛啓動新的客戶端綁定地址成功,收到重傳的FIN包,對新連接造成影響;新啓動的客戶端,若是向相同的服務端發送SYN,因爲服務端處於LAST_ACK,需求的是ACK而不是SYN,因此就會發送RST。
- 因此若主動關閉方最後一次回覆後直接釋放資源,就有可能會對新啓動的新連接造成影響,因此必須等待一段時間,能夠處理有可能重傳的FIN。
- 等待比較合適的時間->2個MSL時間(MSL->報文最大生存週期)。1、處理重傳的FIN包;2、等到本次通信的所有報文都消失在網絡中,避免對新連接造成影響。
TCP三次握手失敗,服務端如何處理?
- 沒有收到SYN,什麼都不做;
- 回覆了SYN+ACK,但長時間沒有收到響應,則超時後發送RST重置連接報文,釋放資源。
一臺主機上出現大量的TIME_WAIT是什麼原因?如何處理?
- TIME_WAIT是主動關閉方出現的,一臺主機上出現大量的TIME_WAIT說明這臺主機主動關閉了大量的連接,常見於一些爬蟲服務器。
- 處理方法:調整TIME_WAIT等待時間,也可使用開啓地址重用的套接字選項->setsockopt。(地址重用->允許套接字綁定使用中的地址端口,常用於防止socket處於TIME_WAIT無法使用相同地址信息進行綁定新的套接字)
一臺主機上出現大量的CLOSE_WAIT是什麼原因?如何處理?
- CLOSE_WAIT是被動關閉方收到FIN請求進行回覆之後的狀態,等待上層程序進行進一步處理。若出現大量CLOSE_WAIT,有可能是被動關閉方主機程序中忘記了最後一步斷開連接後調用close釋放資源。
TCP連接管理中的保活機制:
- tcp通信中,若兩端長時間沒有數據往來,則這時候每隔一段時間,服務端向客戶端發送一個保活探測數據報,要求客戶端進行修復,若連接多次沒有收到響應,則認爲連接斷開。
- 長時間沒有數據往來:默認7200s--->可配置,通過設置套接字選項配置。
- 每隔一段時間:默認75s--->可配置,通過設置套接字選項配置。
- 連續多次無響應:默認9次--->可配置,通過設置套接字選項配置。
可靠傳輸:
- 面向連接;
- 確認應答機制->發送數據後要求對方進行確認回覆,才能知道對方是否收到了這條數據,通過序號與確認序號實現;
- 超時重傳機制->發送數據等待確認響應超時之後,認爲數據丟失,則進行重新傳輸;
- 通過序號/確認序號字段實現數據有序交付;
- 通過校驗和字段校驗數據一致性,不一致則要求對方進行重傳。
- seq:本條數據的起始序號,
- ack:對對方發送數據的確認序號,告訴對方確認序號之前的數據已收到
- 三次握手時,雙方會協商起始序號(上面例子從0開始,實際中不一定,是一個隨機值),三次握手時,雖然數據長度爲0,但是確認序號時對方發送的數據起始序號+1。(ack=seq+1,seq=ack)
- 數據通信時,確認序號是對方發送的數據起始序號+數據長度(ack=seq+len),告訴對方這個確認序號之前的數據都已收到。
- 數據連續發送時,第一條數據丟失,接收方直接收到了第二條和第三條,接收方就不會對第二條和第三條進行回覆->因爲確認序號確認的是這個序號之前的數據全部已收到。這麼做的目的是->防止因爲確認回覆的丟失而導致的重傳。如果因爲網絡原因,後發的數據先到,接收方也會根據協商的起始序號,根據每條數據中的起始序號,將數據在接收緩衝區中進行排序。
額外的丟包問題處理:
- 發送方發送數據過快,接收方來不及處理取出,接收緩衝區滿溢,那麼以後的數據都會被丟棄;
- 發送方發送大量的數據,但是因爲網絡狀態不好導致大量丟包造成重傳。
滑動窗口機制:依靠協議中的窗口大小字段實現流量控制
- 接收方通過協議中的窗口大小字段,告訴發送方,最多可以發送多少數據(窗口大小不大於接收方的接收緩衝區剩餘空間大小->避免了發送的數據太多而緩衝區滿了沒處放的丟包)。
- MSS:最大數據段大小,表示tcp數據通信時一條數據的最大大小,通信時雙方進行協商,取雙方mss中較小的一方作爲最大數據段大小。
- 滑動窗口在發送方維護髮送窗口/接收方維護接收窗口->窗口通過一個後沿序號/前沿序號實現。
- 發送窗口:表示一次最多從後沿到前沿最多發送多少數據->不超過接收方的窗口大小。
- 後沿:所要發送數據的起始序號,後延的移動取決於是否收到數據確認回覆。
- 前沿:根據接收方窗口大小計算的結束序號,取決於接收方響應的窗口大小(前沿減去後沿就是接收方的窗口大小)
- 接收窗口:表示從哪裏開始接收數據,接收到多少序號爲止->不超過剩餘空間大小,進行包序管理,哪個包應該放在緩衝區的什麼位置。
- 後沿:接收數據的起始序號,後沿的移動取決於是否收到了數據。
- 前沿:根據接收緩衝區剩餘空間大小計算得到的接收的數據的結束序號,前沿的移動取決於剩餘空間大小。
- 窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值。
- 操作系統內核爲了維護這個滑動窗口, 需要開闢發送緩衝區來記錄當前還有哪些數據沒有應答; 只有確認應答過的數據, 才能從緩衝區刪掉;
- 窗口越大, 則網絡的吞吐率就越高;
- 停等協議:得到一條回覆,然後才能發送下一條數據。
- 回退n步協議:一條數據丟失了,則需要發送端將丟失這條數據以後的數據都進行重傳。
- 選擇重傳協議:一條數據丟失了,則僅僅針對丟失的這條數據進行重傳。
- 因爲網絡狀態不好,導致發送的數據越多丟包的越多->擁塞控制。
- 擁塞控制:進行網絡探測,以一種慢啓動,快增長的傳輸方式,根據網絡狀態調整發送速度的機制。
提高傳輸性能的方式:
- 快速重傳機制:發送端連續發送多條數據,若接收端接收數據並非是接收後沿數據,則認爲有可能後沿數據丟失了,首先不會進行接收到的數據的確認回覆,其次向發送端間隔連續發送三次後沿數據的重傳請求,要求對方對後沿數據進行重傳。發送方連續收到三條同一重傳請求,則對這條數據進行重傳。
- 爲什麼是三次:避免因爲網絡延遲,數據報的延遲到達,三次可以有一個緩衝時間,若在第二次的時候收到了後沿數據報,則不再發送第三條重傳請求,這時候發送端也不用重傳了。
- 快速重傳,可以一定程度避免發送端必須的超時重傳。
- 延時應答機制:接收方接收到數據之後,並不立即進行確認回覆(因爲如果立即進行確認回覆,因爲接收緩衝區剩餘空間變小了,窗口就變小了,導致傳輸的吞吐量變小了),而是延遲應答,則有可能應答的時候程序在上層已經將數據取出,保證窗口大小不會變小。
- 捎帶應答機制:接收方接收到數據之後,進行確認回覆,確認回覆就是一個報頭中的確認序號進行的,爲了減少空報頭的響應占據帶寬,則使用捎帶應答,在即將要發送的數據頭部中進行上一條接收到的數據的確認回覆。
面向字節流:字節流傳輸服務是面向連接的,有序的一種最小以字節爲傳輸單元的傳輸方式。
- 發送端在send發送數據的時候,並不會立即封裝報頭,而是將數據先放到發送緩衝區中,選擇合適的時候再去從緩衝區中取出合適大小的數據進行傳輸。
- 接收端在recv接收數據的時候,並非一條一條向上交付,而是根據recv想要的數據長度從接收緩衝區中取出指定長度的數據進行交付。
- 優點:傳輸比較靈活,大量小的數據會集合成一條大的數據進行一次性傳輸,減少了IO次數提高了性能(延遲發送可以關閉,可配置),接收方接收也更加靈活,想要多少取多少。
- 缺陷:tcp交付的這條數據可能並非一條完整的數據,也有可能是多條數據(tcp對於上層給與的數據邊界並不敏感,不關注是幾條數據,只關注自己可以傳輸多少字節的數據/recv想要多少字節的數據)