Alamofire學習(一)網絡基礎

Alamofire學習(一)網絡基礎web

Alamofire(二)URLSession算法

Alamofire(三)後臺下載原理數據庫

@TOCswift

網絡基礎知識

1. 網絡架構

1.1網絡OSI七層協議

下面是協議層從底層至頂層的一個模型圖: 瀏覽器

網絡七層協議

  • OSI七層協議
OSI中的層 功能 TCP/IP協議族
應用層 文件傳輸,電子郵件,文件服務,虛擬終端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
表示層 數據格式化,代碼轉換,數據加密 沒有協議
會話層 解除或創建與別的接點的聯繫 沒有協議
傳輸層 提供端對端的接口 TCP,UDP
網絡層 爲數據包選擇路由 IP,ICMP,RIP,OSPF,BGP,IGMP
數據鏈路層 傳輸有地址的幀以及錯誤檢測功能 SLIP,CSLIP,PPP,ARP,RARP,MTU
物理層 以二進制數據形式在物理媒體上傳輸數據 ISO2110,IEEE802,IEEE802.2
  • TCP/IP五層模型的協議
TCP/IP層 網絡設備
應用層
傳輸層 四層交換機、也有工做在四層的路由器
網絡層 路由器、三層交換機
數據鏈路層 網橋(現已不多使用)、以太網交換機(二層交換機)、網卡(其實網卡是一半工做在物理層、一半工做在數據鏈路層)
物理層 中繼器、集線器、還有咱們一般說的雙絞線也工做在物理層

1.1.1.OSI七層協議簡介

OSIOpen System Interconnect的縮寫,意爲開放式系統互聯。安全

OSI七層參考模型的各個層次的劃分遵循下列原則:服務器

一、同一層中的各網絡節點都有相同的層次結構,具備一樣的功能。 二、同一節點內相鄰層之間經過接口(能夠是邏輯接口)進行通訊。 三、七層結構中的每一層使用下一層提供的服務,而且向其上層提供服務。 四、不一樣節點的同等層按照協議實現對等層之間的通訊。markdown

  • 第一層:物理層(PhysicalLayer)

規定通訊設備的機械的、電氣的、功能的和過程的特性,用以創建、維護和拆除物理鏈路鏈接。具體地講,機械 特性規定了網絡鏈接時所需接插件的規格尺寸、引腳數量和排列狀況等;電氣特性規定了在物理鏈接上傳輸bit流時線路上信號電平的大小、阻抗匹配、傳輸速率 距離限制等;功能特性是指對各個信號先分配確切的信號含義,即定義了DTE和DCE之間各個線路的功能;規程特性定義了利用信號線進行bit流傳輸的一組 操做規程,是指在物理鏈接的創建、維護、交換信息是,DTE和DCE雙放在各電路上的動做系列。在這一層,數據的單位稱爲比特(bit)。屬於物理層定義的典型規範表明包括:EIA/TIA RS-23二、EIA/TIA RS-44九、V.3五、RJ-45等。網絡

  • 第二層:數據鏈路層(DataLinkLayer)

在物理層提供比特流服務的基礎上,創建相鄰結點之間的數據鏈路,經過差錯控制提供數據幀(Frame)在信道上無差錯的傳輸,並進行各電路上的動做系列。數據鏈路層在不可靠的物理介質上提供可靠的傳輸。該層的做用包括:物理地址尋址、數據的成幀、流量控制、數據的檢錯、重發等。在這一層,數據的單位稱爲幀(frame)。數據鏈路層協議的表明包括:SDLC、HDLC、PPP、STP、幀中繼等。數據結構

數據鏈路層協議:

image

  • 第三層:網絡層(Network layer)

在計算機網絡中進行通訊的兩個計算機之間可能會通過不少個數據鏈路,也可能還要通過不少通訊子網。網絡層的任務就是選擇合適的網間路由和交換結點, 確保數據及時傳送。網絡層將數據鏈路層提供的幀組成數據包,包中封裝有網絡層包頭,其中含有邏輯地址信息- -源站點和目的站點地址的網絡地址。如 果你在談論一個IP地址,那麼你是在處理第3層的問題,這是「數據包」問題,而不是第2層的「幀」。IP是第3層問題的一部分,此外還有一些路由協議和地 址解析協議(ARP)。有關路由的一切事情都在這第3層處理。地址解析和路由是3層的重要目的。網絡層還能夠實現擁塞控制、網際互連等功能。在這一層,數據的單位稱爲數據包(packet)。網絡層協議的表明包括:IP、IPX、RIP、OSPF等。

網絡層協議:

網絡層協議
經常使用網絡層協議:

網絡層協議 功能
IP(Internet Protocol) IP爲網絡層最主要的協議,其功能即爲網絡層的主要功能,一是提供邏輯編址,二是提供路由功能,三是報文的封裝和解封裝。ICMP、ARP、RARP協議輔助IP工做。
ICMP(Internet Control Message Protocol) 是一個管理協議併爲IP提供信息服務,ICMP消息承載在IP報文中。
ARP(Address Resolution Protocol) 實現IP地址到硬件地址的動態映射,即根據已知的IP地址得到相應的硬件地址。
RARP(Reverse Address Resolution Protocol) 實現硬件地址到IP地址的動態映射,即根據已知的硬件地址得到相應的IP地址。
  • 第四層:傳輸層 (Transport layer)

第4層的數據單元也稱做數據包(packets)。可是,當你談論TCP等具體的協議時又有特殊的叫法,TCP的數據單元稱爲段 (segments)而UDP協議的數據單元稱爲「數據報(datagrams)」。這個層負責獲取所有信息,所以,它必須跟蹤數據單元碎片、亂序到達的 數據包和其它在傳輸過程當中可能發生的危險。第4層爲上層提供端到端(最終用戶到最終用戶)的透明的、可靠的數據傳輸服務。所爲透明的傳輸是指在通訊過程當中 傳輸層對上層屏蔽了通訊傳輸系統的具體細節。傳輸層協議的表明包括:TCP、UDP、SPX等。 只在通訊雙方的節點上(好比計算機終端)進行處理,而無需在路由器上處理,傳輸層是OSI中最重要、最關鍵的一層,是惟一負責整體的數據傳輸和數據控制的一層;

傳輸層提供端到端的交換數據的機制,檢查分組編號與次序,傳輸層對其上三層如會話層等,提供可靠的傳輸服務,對網絡層提供可靠的目的地站點信息主要功能

在這一層,數據的單位稱爲數據段(segment)

主要功能:

①:爲端到端鏈接提供傳輸服務

②:這種傳輸服務分爲可靠和不可靠的,其中Tcp是典型的可靠傳輸,而Udp則是不可靠傳輸

③:爲端到端鏈接提供流量控制,差錯控制,服務質量(Quality of Service,QoS)等管理服務

包括的協議以下:

TCP:傳輸控制協議,傳輸效率低,可靠性強

UDP:用戶數據報協議,適用於傳輸可靠性要求不高,數據量小的數據(好比QQ)

DCCP、SCTP、RTP、RSVP、PPTP等協議

更多詳情參考

傳輸層協議:

傳輸層協議

  • 第五層:會話層(Session layer)

這一層也能夠稱爲會晤層或對話層,在會話層及以上的高層次中,數據傳送的單位再也不另外命名,而是統稱爲報文。會話層不參與具體的傳輸,它提供包括訪問驗證和會話管理在內的創建和維護應用之間通訊的機制。如服務器驗證用戶登陸即是由會話層完成的。

  • 第六層:表示層(Presentation layer)

這一層主要解決擁護信息的語法表示問題。它將欲交換的數據從適合於某一用戶的抽象語法,轉換爲適合於OSI系統內部使用的傳送語法。即提供格式化的表示和轉換數據服務。數據的壓縮和解壓縮, 加密和解密等工做都由表示層負責。

  • 第七層:應用層(Application layer)

應用層爲操做系統或網絡應用程序提供訪問網絡服務的接口。應用層協議的表明包括:Telnet、FTP、HTTP、SNMP等。

主要功能:

  1. 爲用戶提供接口、處理特定的應用;
  2. 數據加密、解密、壓縮、解壓縮;
  3. 定義數據表示的標準。

應用層協議:

應用層協議

應用層包含的協議及做用:

應用層協議 功能
超文本傳輸協議HTTP 這是一種最基本的客戶機/服務器的訪問協議;瀏覽器向服務器發送請求,而服務器迴應相應的網頁
文件傳送協議FTP 提供交互式的訪問,基於客戶服務器模式,面向鏈接 使用TCP可靠的運輸服務主要功能:減小/消除不一樣操做系統下文件的不兼容性
遠程登陸協議TELNET 客戶服務器模式,能適應許多計算機和操做系統的差別,網絡虛擬終端NVT的意義
簡單郵件傳送協議SMTP Client/Server模式,面向鏈接 基本功能:寫信、傳送、報告傳送狀況、顯示信件、接收方處理信件
DNS域名解析協議 DNS是一種用以將域名轉換爲IP地址的Internet服務
簡單文件傳送協議TFTP 客戶服務器模式,使用UDP數據報,只支持文件傳輸,不支持交互,TFTP代碼佔內存小
簡單網絡管理協議(SNMP) SNMP模型的4個組件:被管理結點、管理站、管理信息、管理協議 SNMP代理:運行SNMP管理進程的被管理結點,對象:描述設備的變量,管理信息庫(MIB):保存全部對象的數據結構
DHCP動態主機配置協議 發現協議中的引導文件名、空終止符、屬名或者空,DHCP供應協議中的受限目錄路徑名 Options –可選參數字段,參考定義選擇列表中的選擇文件

1.1.2.OSI七層做用

下圖表述了簡單的每一個分層的做用:

OSI七層做用
一張圖片歸納七層協議
image

2 TCP/IP協議

TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)協議屬於傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它提供的服務包括數據流傳送、可靠性、有效流控、全雙工操做和多路復 用。經過面向鏈接、端到端和可靠的數據包發送。通俗說,它是事先爲所發送的數據開闢出鏈接好的通道,而後再進行數據發送;而UDP則不爲IP提供可靠性、 流控或差錯恢復功能。通常來講,TCP對應的是可靠性要求高的應用,而UDP對應的則是可靠性要求低、傳輸經濟的應用。TCP支持的應用協議主要 有:Telnet、FTP、SMTP等;UDP支持的應用層協議主要有:NFS(網絡文件系統)、SNMP(簡單網絡管理協議)、DNS(主域名稱系 統)、TFTP(通用文件傳輸協議)等. TCP/IP協議與低層的數據鏈路層和物理層無關,這也是TCP/IP的重要特色

2.1. TCP/IP協議數據封裝

TCP/IP每一層都讓數據得以經過網絡進行傳輸,這些層之間使用PDU(協議數據單元)彼此交換信息,確保網絡設備之間可以通訊

A. 傳輸層數據中加入TCP報頭後獲得PDU被稱爲segment(數據段); B. 數據段被傳遞給網絡層,網絡層添加IP報頭獲得的PDU被稱爲packet(數據包); C. 數據包被傳遞到數據鏈路層,封裝數據鏈路層報頭獲得的PDU被稱爲frame(數據幀); D. 幀被轉換爲比特,經過網絡介質傳輸。

這種協議棧向下傳遞數據,並添加報頭和報尾的過程稱爲封裝,數據被封裝並經過網絡傳輸後,接收設備將刪除添加的信息,並根據報頭中的信息決定如何將數據沿協議棧上傳給合適的應用程序,這個過程稱爲解封裝。不一樣設備的對等層之間依靠封裝和解封裝來實現相互間的通訊。

2.2. TCP/IP協議數據封裝過程

  1. 先看一張圖以下:

    TCP/IP協議數據封裝過程
    以傳輸層採用TCP或者UPD、網絡層採用IP、鏈路層採用Ethernet爲例,能夠看到TCP/IP中報文的封裝過程如上圖所示。用戶數據通過應用層協議封裝後傳遞給傳輸層,傳輸層封裝TCP頭部,交給網絡層,網絡層封裝IP頭部後,再交給數據鏈路層,數據鏈路層封裝Ethernet幀頭和幀尾,交給物理層,物理層以比特流的形式將數據發送到物理線路上。

  2. TCP數據包格式

    TCP數據格式

  3. TCP首部格式

    TCP首部格式

  • 上面圖TCP數據包字段簡介 TCP使用IP做爲網絡層協議,TCP數據段被封裝在一個IP數據包內。TCP數據段由TCP Head(頭部)和TCP Data(數據)組成。

    TCP最多有60個字節的首部,若是沒有任選字段,正常的長度是20字節。TCP Head如上圖標識的一些字段組成,這裏列出幾個經常使用的字段。

字段 功能 備註
16位源端口號 TCP會爲源應用程序分配一個源端口號 -
16位目的端口號 目的應用程序的端口號。每一個TCP段都包含源和目的端的端口號,用於尋找發端和收端應用進程。這兩個值加上IP首部中的源端IP地址和目的端IP地址能夠惟一肯定一個TCP鏈接。 備註
32位序列號 用於標識從TCP發端向TCP收端發送的數據字節流。 -
32位確認序列號 確認序列號包含發送確認的一端所指望收到的下一個序號。確認序列號爲上次成功收到的數據序列號加1。 -
4位首部長度 表示首部佔32bit字的數目。由於TCP首部的最大長度爲60字節 備註
16位窗口大小 表示接收端指望接收的字節,因爲該字段爲16位,於是窗口大小最大值爲65535字節。 -
16位檢驗和 檢驗和覆蓋了整個TCP報文段,包括TCP首部和TCP數據。該值由發端計算和存儲並由接收端進行驗證 -
  • 6位標誌位
字段 功能 備註
UNG標誌 表示緊急指針是否有效
ACK標誌 表示確認號是否有效。咱們將攜帶ACK標誌的TCP報文段爲確認 報文段
PSH標誌 提示接收端應用程序應該當即從TCP接收緩衝區中讀走數據,爲接收後續數據騰出空間
RST標誌 表示要求對方從新創建鏈接。咱們將攜帶RST標誌的TCP報文段爲復位報文段
SYN標誌 表示請求創建一個鏈接。咱們將攜帶SYN標誌的TCP報文段爲同步報文段
FIN標誌 表示通知對方本端要關閉鏈接了。咱們將攜帶FIN標誌的TCP報文段爲結束報文段

2.3. TCP的三次握手(創建鏈接)和四次揮手(斷開鏈接)

2.3.1 TCP的三次握手

2.3.1.1 TCP的三次握手簡介

TCP的三次握手

  1. 如上圖,咱們能夠清楚的看到三次握手的過程,具體握手流程以下:
  • 第一次: 客戶端 - - > 服務器 此時服務器知道了客戶端要創建鏈接了
  • 第二次: 客戶端 < - - 服務器 此時客戶端知道服務器收到鏈接請求了
  • 第三次: 客戶端 - - > 服務器 此時服務器知道客戶端收到了本身的迴應 到這裏, 就能夠認爲客戶端與服務器已經創建了鏈接.

爲了更加清楚的弄清過程能夠再看一下這張動態圖:

三次握手動態圖

圖裏面的SYN,ACK,PSH,FIN,RST,URG 就是前面已經解釋過的TCP包首部格式中的6位標誌位。

字段 功能 備註
UNG標誌 表示緊急指針是否有效
ACK標誌 表示確認號是否有效。咱們將攜帶ACK標誌的TCP報文段爲確認 報文段
PSH標誌 提示接收端應用程序應該當即從TCP接收緩衝區中讀走數據,爲接收後續數據騰出空間
RST標誌 表示要求對方從新創建鏈接。咱們將攜帶RST標誌的TCP報文段爲復位報文段
SYN標誌 表示請求創建一個鏈接。咱們將攜帶SYN標誌的TCP報文段爲同步報文段
FIN標誌 表示通知對方本端要關閉鏈接了。咱們將攜帶FIN標誌的TCP報文段爲結束報文段
Sequence number Seq序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記
Acknowledge number Ack序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效,Ack=Seq+1
2.3.1.2 TCP的三次握手分析

如上圖中咱們能夠大體看到3次握手的過程:

  1. 剛開始, 客戶端和服務器都處於 CLOSE 狀態. 此時, 客戶端向服務器主動發出鏈接請求, 服務器被動接受鏈接請求.
  2. TCP服務器進程先建立傳輸控制塊TCB, 時刻準備接受客戶端進程的鏈接請求, 此時服務器就進入了 LISTEN(監聽)狀態 。
  3. TCP客戶端進程也是先建立傳輸控制塊TCB, 而後向服務器發出鏈接請求報文,此時報文首部中的同步標誌位SYN=1, 同時選擇一個初始序列號 seq = x, 此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定, SYN報文段(SYN=1的報文段)不能攜帶數據,但須要消耗掉一個序號。
  4. TCP服務器收到請求報文後, 若是贊成鏈接, 則發出確認報文。確認報文中的 ACK=1, SYN=1, 確認序號是 x+1, 同時也要爲本身初始化一個序列號 seq = y, 此時, TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據, 可是一樣要消耗一個序號。
  5. TCP客戶端進程收到確認後還, 要向服務器給出確認。確認報文的ACK=1,確認序號是 y+1,本身的序列號是 x+1.
  6. 此時,TCP鏈接創建,客戶端進入ESTABLISHED(已創建鏈接)狀態。當服務器收到客戶端的確認後也進入ESTABLISHED狀態,此後雙方就能夠開始通訊了。

這裏不少人確定有疑問,爲啥是三次,不是兩次或者四次呢?

  • 爲啥不用兩次?

答:主要是爲了防止已經失效的鏈接請求報文忽然又傳送到了服務器,從而產生錯誤。若是使用的是兩次握手創建鏈接,假設有這樣一種場景,客戶端發送的第一個請求鏈接而且沒有丟失,只是由於在網絡中滯留的時間太長了,因爲TCP的客戶端遲遲沒有收到確認報文,覺得服務器沒有收到,此時從新向服務器發送這條報文,此後客戶端和服務器通過兩次握手完成鏈接,傳輸數據,而後關閉鏈接。此時以前滯留的那一次請求鏈接,由於網絡通暢了, 到達了服務器,這個報文本該是失效的,可是,兩次握手的機制將會讓客戶端和服務器再次創建鏈接,這將致使沒必要要的錯誤和資源的費。 若是採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文而且回覆了確認報文,可是客戶端不會再次發出確認。因爲服務器收不到確認,就知道客戶端並無請求鏈接。

  • 爲啥不用四次?

答:這個很好解釋,由於三次已經能夠知足須要了, 四次就多餘了.屬於浪費資源。

2.3.2 TCP的四次揮手

下來看四次揮手的一個時序圖:

TCP的四次揮手

  • 咱們先來分析一下上圖的四次握手的過程:
  1. 數據傳輸完畢後,雙方均可以釋放鏈接. 此時客戶端和服務器都是處於ESTABLISHED狀態,而後客戶端主動斷開鏈接,服務器被動斷開鏈接.
  2. 客戶端進程發出鏈接釋放報文,而且中止發送數據。 釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即便不攜帶數據,也要消耗一個序號。
  3. 服務器收到鏈接釋放報文,發出確認報文,ACK=1,確認序號爲 u+1,而且帶上本身的序列號seq=v,此時服務端就進入了CLOSE-WAIT(關閉等待)狀態。 TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,可是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
  4. 客戶端收到服務器的確認請求後,此時客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送鏈接釋放報文(在這以前還須要接受服務器發送的最終數據)
  5. 服務器將最後的數據發送完畢後,就向客戶端發送鏈接釋放報文,FIN=1,確認序號爲v+1,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
  6. 客戶端收到服務器的鏈接釋放報文後,必須發出確認,ACK=1,確認序號爲w+1,而本身的序列號是u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP鏈接尚未釋放,必須通過2∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
  7. 服務器只要收到了客戶端發出的確認,當即進入CLOSED狀態。一樣,撤銷TCB後,就結束了此次的TCP鏈接。能夠看到,服務器結束TCP鏈接的時間要比客戶端早一些
  • 經過一張動態圖來加深理解:
    TCP四次揮手過程
    說白了整個創建鏈接三次握手過程就像這樣的場景: (1). 一男一女在一個咖啡廳相親,男的(客戶端)先問:你以爲我怎麼樣?(第一次握手) (2). 女的回答:我以爲你很帥 而後 問了男的一句:你以爲我漂亮麼?,想確認一下男方的意見。(第二次握手) (3). 男的聽到女的回答內心早就樂開了花,恨不得立刻牽着女方的小手,回答:我以爲你就是個人女神,咱們在一塊兒吧 ,到此牽手成功。(第三次握手)創建鏈接。 (4). 若是上面3步少了任何一步都不能牽手,要兩我的都贊成,都確認後才能創建鏈接。

而四次揮手的過程,就相似於這樣的場景: (1). 相親的男女在一塊兒相處了一陣子,開始鬧矛盾了,女的性子比較急一點,先提出分手(女方至關於客戶端)女的說:我受夠你了,你就是個矮矬窮,我要的白馬王子應該是高富帥纔對。 (第一次揮手:FIN M)第一次揮手後進入了 Finish_Wait_1狀態 (2). 男的以爲女的還挺好的,很漂亮,就是性格有點倔強,不想分手,男的還想給女的說不少話去挽留。此時男的說:我收到你要分手的信息了 (ack+1),而後又向女的說了不少挽留的話,此時只有男的向女的說話(服務器--》客戶端)半雙工。(第二次揮手: ack M+1) (服務器收到第一次揮手的信息後,就進入Close_Wait狀態,可是還能夠像客戶端繼續發送沒有發送完的信息,而後發送一個確認包:ack = M+1) (3). 男的思考了很近說了不少話挽留都無效,而後男絕望的說了一句:真的要分手嗎 再次確認一下 (FIN N)而後等待女方的回答,再也不向女方說話了。(第三次揮手)(此時服務器進入了LAST_ACK最後確認狀態)

(4). 女的聽到男的說的話以後, 有些猶豫了,若是她想反悔了,就能夠跟男的說:我不想分手了 這樣分手就失敗了,若是她仍是堅持她本身原生相反,堅定要分手,她就會說:我想了好久,咱們真的不合適,咱們仍是分手吧 (第四次揮手:ACK=1, ack = K +1 )。(此時客戶端進入Time_Wait狀態,等待一個2MSL時間就會關閉鏈接) (5). 男方收到女方的信息,若是男方決定要分手,就會直接Close()關閉通話狀態,沒必要再發信息給女方了,若是男方決定不分手了,就會再跟女的說:咱們不分手了到此分手就已失敗結束了。(這裏服務器會比客戶端 先關閉鏈接狀態,客戶端須要等待一個最長2MSL的時間)

  • 爲何最後客戶端還要等待 2*MSL的時間呢?

MSL(Maximum Segment Lifetime),TCP容許不一樣的實現能夠設置不一樣的MSL值。

第一,保證客戶端發送的最後一個ACK報文可以到達服務器,由於這個ACK報文可能丟失,站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端尚未給我回應,應該是我發送的請求斷開報文它沒有收到,因而服務器又會從新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接着給出迴應報文,而且會重啓2MSL計時器。

第二,防止相似與「三次握手」中提到了的「已經失效的鏈接請求報文段」出如今本鏈接中。客戶端發送完最後一個確認報文後,在這個2MSL時間中,就可使本鏈接持續的時間內所產生的全部報文段都從網絡中消失。這樣新的鏈接中不會出現舊鏈接的請求報文。

  • 爲何是三次握手,兩次能夠嗎?

解答: 首先,兩次顯然是不能夠的。這類問題能夠舉一個反例來講明狀況。 謝希仁版《計算機網絡》中的例子是這樣的,「已失效的鏈接請求報文段」的產生在 這樣一種狀況下:client 發出的第一個鏈接請求報文段並無丟失,而是在某個網絡結 點長時間的滯留了,以至延誤到鏈接釋放之後的某個時間纔到達 server。原本這是一個 早已失效的報文段。但 server 收到此失效的鏈接請求報文段後,就誤認爲是 client 再次 發出的一個新的鏈接請求。因而就向 client 發出確認報文段,贊成創建鏈接。假設不採 用「三次握手」,那麼只要 server 發出確認,新的鏈接就創建了。因爲如今 client 並無 發出創建鏈接的請求,所以不會理睬 server 的確認,也不會向 server 發送數據。但 server 卻覺得新的運輸鏈接已經創建,並一直等待 client 發來數據。這樣,server 的不少資源 就白白浪費掉了。採用「三次握手」的辦法能夠防止上述現象發生。例如剛纔那種狀況, client 不會向 server 的確認發出確認。server 因爲收不到確認,就知道 client 並無要 求創建鏈接。」。主要目的防止 server 端一直等待,浪費資源。 擴展下,揮手通常是四次爲何?揮手在某些狀況下三次能完成嗎? 某些狀況下,三次是能夠完成揮手的,當本端關閉了鏈接,剛好也同時收到了對方 的 FIN 報文,此時能夠把本身的 FIN 和給對端的確認 ACK 合在一塊兒發送。就變成了三 次。

  • 爲何創建鏈接是三次握手,關閉鏈接確是四次揮手呢?

1.創建鏈接的時候, 服務器在LISTEN狀態下,收到創建鏈接請求的SYN報文後,把ACKSYN放在一個報文裏發送給客戶端。

2.關閉鏈接時,服務器收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,而本身也未必所有數據都發送給對方了,因此己方能夠當即關閉,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACKFIN通常都會分開發送,從而致使多了一次。

  • 爲何TIME_WAIT狀態須要通過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

答:雖然按道理,四個報文都發送完畢,咱們能夠直接進入CLOSE狀態了,可是咱們必須假象網絡是不可靠的,有能夠最後一個ACK丟失。因此TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最後的ACK回覆,但該ACK可能丟失。Server若是沒有收到ACK,將不斷重複發送FIN片斷。因此Client不能當即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK以後進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。若是在該時間內再次收到FIN,那麼Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片斷在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。若是直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP鏈接。

  • 三次握手的做用是?

1) 使得通信雙發都作好通信的準備 2) 告訴對端本端通信所選用的報文標識號 3) 防止已失效的鏈接請求報文段又忽然傳遞到了服務端,從而產生錯誤

  • 三次握手那個階段容易出現攻擊?

解答: 比較典型的是 syn 泛洪攻擊,或叫 syn 溢出攻擊。 syn 溢出攻擊,即出如今第二個階段,若是客戶機僞造出大量第一次的 sys 同步報 文,服務端就會依次耗掉不少資源來保存客戶端的信息,並進行確認,實際確認是會失 敗的,但失敗是須要必定時間,由於服務端會連續屢次進行第二次握手確認後才認定失 敗。那麼短期有大量 syn 同步報文涌向服務端,服務器資源可能被耗盡,就可能致使 正常的客戶端得不到響應而失敗。

  • 三次握手那個階段會出現異常?

解答: 第二個階段可能異常,若是服務器相應的端口未打開,會回覆 RST 復位報文,握 手失敗。此外,listen 建立的監聽隊列達到上限,也可能失敗。

  • TIME_WAIT 和 CLOSE_WAIT 有什麼區別?

解答: CLOSE_WAIT 是被動關閉的一端在接收到對端關閉請求(FIN 報文段)而且將 ACK 發送出去後所處的狀態,這種狀態表示:收到了對端關閉的請求,可是本端尚未完成 工做,還未關閉。 TIME_WAIT 狀態是主動關閉的一端在本端已經關閉的前期下,收到對端的關閉請 求(FIN 報文段)而且將 ACK 發送出去後所處的狀態,這種狀態表示:雙方都已經完成 工做,只是爲了確保遲來的數據報能被是被並丟棄,可靠的終止 TCP 鏈接。

  • TIME_WAIT 狀態存在的意義?

解答: TIME_WAIT 狀態是:主動斷開鏈接的一端收到對端的 FIN 報文段而且將 ACK 報文 段發出後的一種狀態。 意義: 1) 保證遲來的報文段能被識別並丟棄。 2) 保證可靠的終止 TCP 鏈接。保證對端能收到最後的一個 ACK,若是 ACK 丟失, 在TIME_WAIT狀態本端還能夠接受到對端重傳的FIN報文段並從新發送ACK。 因此 TIME_WAIT 的存在時間爲 2MSL。

  • 若是已經創建了鏈接, 可是客戶端突發故障了怎麼辦?

TCP設有一個保活計時器,顯然,客戶端若是出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會從新復位這個計時器,時間一般是設置爲2小時,若兩小時尚未收到客戶端的任何數據,服務器就會發送一個探測報文段,之後每隔75分鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉鏈接。

2.4. TCP確認應答機制(ACK機制)

ACK確認

  • TCP將每一個字節的數據都進行了編號, 即爲序列號.

  • 每個ACK都帶有對應的確認序列號, 意思是告訴發送者, 我已經收到了哪些數據; 下一次你要從哪裏開始發. 好比, 客戶端向服務器發送了1005字節的數據, 服務器返回給客戶端的確認序號是1003, 那麼說明服務器只收到了1-1002的數據. 1003, 1004, 1005都沒收到. 此時客戶端就會從1003開始重發.

2.5. TCP超時重傳機制

TCP重傳

  • 主機A發送數據給B以後, 可能由於網絡擁堵等緣由, 數據沒法到達主機B 若是主機A在一個特定時間間隔內沒有收到B發來的確認應答, 就會進行重發 可是主機A沒收到確認應答也多是ACK丟失了.

  • 這種狀況下, 主機B會收到不少重複數據. 那麼TCP協議須要識別出哪些包是重複的, 而且把重複的丟棄. 這時候利用前面提到的序列號, 就能夠很容易作到去重.

  • 超時時間如何肯定? 最理想的狀況下, 找到一個最小的時間, 保證 「確認應答必定能在這個時間內返回」. 可是這個時間的長短, 隨着網絡環境的不一樣, 是有差別的. 若是超時時間設的太長, 會影響總體的重傳效率; 若是超時時間設的過短, 有可能會頻繁發送重複的包. TCP爲了保證任何環境下都能保持較高性能的通訊, 所以會動態計算這個最大超時時間.

Linux中(BSD Unix和Windows也是如此), 超時以500ms爲一個單位進行控制, 每次斷定超時重發的超時時間都是500ms的整數倍. 若是重發一次以後, 仍然得不到應答, 等待 2500ms 後再進行重傳. 若是仍然得不到應答, 等待 4500ms 進行重傳. 依次類推, 以指數形式遞增. 累計到必定的重傳次數, TCP認爲網絡異常或者對端主機出現異常, 強制關閉鏈接.

2.6. TCP滑動窗口機制

  1. 先看下面一張圖:
    TCP滑動窗口機制
    TCP滑動窗口技術經過動態改變窗口大小來調節兩臺主機間的數據傳輸。每一個TCP/IP主機支持全雙工數據傳輸,所以TCP有兩個滑動窗口:一個用於接收數據,另外一個用於發送數據。TCP使用確定確認技術,其確認號指的是下一個所期待的字節。
  2. 如圖中所示以數據單方向發送爲例,介紹滑動窗口如何實現流量控制。服務器端向客戶端發送4個大小爲1024字節的數據段,其中發送端的窗口大小爲4096,客戶端到以ACK4097響應,窗口大小調整爲2048,代表客戶端(即接收端)緩衝區只能處理2048個字節的數據段。因而發送端改變其發送速率。發送接收端可以接收的數據段大小2048的數據段。
  • 滑動窗口

窗口大小指的是無需等待確認應答就能夠繼續發送數據的最大值. 上圖的窗口大小就是4000個字節 (四個段). 發送前四個段的時候, 不須要等待任何ACK, 直接發送 收到第一個ACK確認應答後, 窗口向後移動, 繼續發送第五六七八段的數據… 由於這個窗口不斷向後滑動, 因此叫作滑動窗口. 操做系統內核爲了維護這個滑動窗口, 須要開闢發送緩衝區來記錄當前還有哪些數據沒有應答 只有ACK確認應答過的數據, 才能從緩衝區刪掉.

  • 若是出現了丟包, 那麼該如何進行重傳呢? 此時分兩種狀況討論:

    • 1, 數據包已經收到, 但確認應答ACK丟了. 這種狀況下, 部分ACK丟失並沒有大礙, 由於還能夠經過後續的ACK來確認對方已經收到了哪些數據包.

    • 2, 數據包丟失 . 以下圖:當某一段報文丟失以後, 發送端會一直收到 1001 這樣的ACK, 就像是在提醒發送端 「我想要的是 1001」 若是發送端主機連續三次收到了一樣一個 「1001」 這樣的應答, 就會將對應的數據 1001 - 2000 從新發送 這個時候接收端收到了 1001 以後, 再次返回的ACK就是7001了 由於2001 - 7000接收端其實以前就已經收到了, 被放到了接收端操做系統內核的接收緩衝區中.

2.7. TCP流量控制

2.7.1 什麼是流量控制

接收端處理數據的速度是有限的. 若是發送端發的太快, 致使接收端的緩衝區被填滿, 這個時候若是發送端繼續發送, 就會形成丟包, 進而引發丟包重傳等一系列連鎖反應. 所以TCP支持根據接收端的處理能力, 來決定發送端的發送速度. 這個機制就叫作 流量控制(Flow Control)

2.7.2 流量控制流程

  1. 如上圖,流程概述:
  • 接收端將本身能夠接收的緩衝區大小放入 TCP 首部中的 「窗口大小」 字段,
  • 經過ACK通知發送端;
  • 窗口大小越大, 說明網絡的吞吐量越高;
  • 接收端一旦發現本身的緩衝區快滿了, 就會將窗口大小設置成一個更小的值通知給發送端;
  • 發送端接受到這個窗口大小的通知以後, 就會減慢本身的發送速度;
  • 若是接收端緩衝區滿了, 就會將窗口置爲0;
  • 這時發送方再也不發送數據, 可是須要按期發送一個窗口探測數據段, 讓接收端把窗口大小再告訴發送端.
  1. 那麼接收端如何把窗口大小告訴發送端呢?

咱們的TCP首部中, 有一個16位窗口大小字段, 就存放了窗口大小的信息; 16位數字最大表示65536, 那麼TCP窗口最大就是65536字節麼? 實際上, TCP首部40字節選項中還包含了一個窗口擴大因子M, 實際窗口大小是窗口字段的值左移 M 位(左移一位至關於乘以2).

2.8. TCP擁塞控制

擁塞控制, 歸根結底是TCP協議想盡量快的把數據傳輸給對方, 可是又要避免給網絡形成太大壓力的折中方案.

  • 雖然TCP有了滑動窗口這個大殺器, 可以高效可靠地發送大量數據. 可是若是在剛開始就發送大量的數據, 仍然可能引起一些問題. 由於網絡上有不少計算機, 可能當前的網絡狀態已經比較擁堵. 在不清楚當前網絡狀態的狀況下, 貿然發送大量數據, 頗有可能雪上加霜.

  • 所以, TCP引入 慢啓動 機制, 先發少許的數據, 探探路, 摸清當前的網絡擁堵狀態之後, 再決定按照多大的速度傳輸數據.

2.8.1 擁塞窗口

  • 發送開始的時候, 定義擁塞窗口大小爲1;
  • 每次收到一個ACK應答, 擁塞窗口加1;
  • 每次發送數據包的時候, 將擁塞窗口和接收端主機反饋的窗口大小作比較, 取較小的值做爲實際發送的窗口
  • 當TCP開始啓動的時候, 慢啓動閾值等於窗口最大值
  • 在每次超時重發的時候, 慢啓動閾值會變成原來的一半, 同時擁塞窗口置回1
  • 像上面這樣的擁塞窗口增加速度, 是指數級別的. 「慢啓動」 只是指初使時慢, 可是增加速度很是快. 爲了避免增加得那麼快, 此處引入一個名詞叫作慢啓動的閾值, 當擁塞窗口的大小超過這個閾值的時候, 再也不按照指數方式增加, 而是按照線性方式增加.
  • 少許的丟包, 咱們僅僅是觸發超時重傳;
  • 大量的丟包, 咱們就認爲是網絡擁塞;
  • 當TCP通訊開始後, 網絡吞吐量會逐漸上升;
  • 隨着網絡發生擁堵, 吞吐量會馬上降低.

3.UDP協議

3.1 TCP,UDP區別

TCP,UDP區別

對比項 TCP UDP
是否面向鏈接 面向鏈接 面向非鏈接
傳輸可靠性 可靠 不可靠
應用場合 傳輸大量數據 少許數據
傳輸速度
  • TCP提供面向鏈接的、可靠的字節流服務。面向鏈接意味着使用TCP協議做爲傳輸層協議的兩個應用之間在相互交換數據以前必須創建一個TCP鏈接。TCP經過確認、校驗、重組等機制爲上層應用提供可靠的傳輸服務。可是TCP鏈接的創建以及確認、校驗等機制都須要耗費大量的工做而且會帶來大量的開銷。

  • UDP提供簡單的、面向數據報的服務。UDP不保證可靠性,即不保證報文可以到達目的地。UDP適用於更關注傳輸效率的應用,如SNMP、Radius等,SNMP監控網絡並斷續發送告警等消息,若是每次發送少許信息都須要創建TCP鏈接,無疑會下降傳輸效率,因此諸如SNMP、Radius等更注重傳輸效率的應用程序都會選擇UDP做爲傳輸層協議。另外,UDP還適用於自己具有可靠性機制的應用層協議。

3.2 UDP協議簡介

  1. UDP協議特色:
  • UDP爲應用程序提供面向無鏈接的服務。傳輸數據以前源端和目的端不須要創建鏈接。
  • 不須要維持鏈接狀態,收發狀態等,所以服務器可同時向多個客戶端傳輸相同的消息。
  • UDP適用於對傳輸效率要求高的運用。
  1. UDP首部格式
    UDP首部格式
    UDP和TCP同樣都使用IP做爲網絡層協議,TCP數據報被封裝在一個IP數據包內。因爲UDP不象TCP同樣提供可靠的傳輸,所以UDP的報文格式相對而言較簡單。

如上圖UDP首部格式,下表對字段含義作一下簡介:

字段 做用 說明
16位源端口號 爲源端應用程序分配的一個源端口號
16位目的端口號 目的應用程序的端口號
16位UDP長度 是指UDP首部和UDP數據的字節長度。該字段的最小值爲8
16位UDP檢驗和 該字段提供與TCP檢驗和一樣的功能,只不過在UDP協議中該字段是可選的

4 IP協議

4.1 IP 包數據格式

  1. IP packet 結構以下圖:網絡層收到傳輸層的TCP數據段後會再加上網絡層IP頭部信息。普通的IP頭部固定長度爲20個字節(不包含IP選項字段)。 IP報文頭主要由如下字段組成:報文長度是指頭部佔32比特字的個數,包括任何選項。因爲它是一個4比特字段,24=16,除掉全0項共有15個有效值比特字段,其中最大值也爲15,表示頭部佔15個32比特。所以32*15/8=60字節,頭部最長爲60字節。
    IP 包數據格式
    下面表對上圖字段作一下介紹:
字段 做用 說明
版本號(Version) 字段標明瞭IP協議的版本號,目前的協議版本號爲4。下一代IP協議的版本號爲6。
8比特的服務類型(TOS,Type of Service) 字段包括一個3比特的優先權字段(COS,Class of Service),4比特TOS字段和1比特未用位。4比特TOS分別表明最小時延、最大吞吐量、最高可靠性和最小費用。
總長度(Total length) 是整個IP數據報長度,包括數據部分。因爲該字段長16比特,因此IP數據報最長可達65535字節。儘管能夠傳送一個長達65535字節的IP數據報,可是大多數的鏈路層都會對它進行分片。並且,主機也要求不能接收超過576字節的數據報。UDP限制用戶數據報長度爲512字節,小於576字節。而事實上如今大多數的實現(特別是那些支持網絡文件系統NFS的實現)容許超過8192字節的IP數據報。
標識符(Identification) 字段惟一地標識主機發送的每一份數據包。一般每發送一份報文它的值就會加1
生存時間(TTL,Time to Live) 字段設置了數據包能夠通過的路由器數目。一旦通過一個路由器,TTL值就會減1,當該字段值爲0時,數據包將被丟棄。
協議 協議字段肯定在數據包內傳送的上層協議,和端口號相似,IP協議用協議號區分上層協議。TCP協議的協議號爲6,UDP協議的協議號爲17。
報頭校驗和(Head checksum) 字段計算IP頭部的校驗和,檢查報文頭部的完整性。
源IP地址 字段標識數據包的源端設備
目的IP地址 字段標識數據包的目的端設備IP地址信息
IP選項
  1. 以太網幀格式 Ethernet frame
    以太網幀格式
    如上圖以太網頭部是由三個字段組成:DMAC , SMAC, L/T(LENGTH/TYPE字段)
  • 以太網頭部字段說明:
字段 做用 說明
DMAC 表示目的終端MAC地址。
SMAC 表示源端MAC地址
LENGTH/TYPE字段 根據值的不一樣有不一樣的含義:當LENGHT/TYPE > 1500時,表明該數據幀的類型(好比上層協議類型)常見的協議類型有:0X0800 IP數據包0X0806 ARP請求/應答報文0X8035 RARP請求/應答報文。當LENGTH/TYPE < 1500時,表明該數據幀的長度。

5. HTTP協議

5.1 HTTP簡介

HTTP全稱是HyperText Transfer Protocal,即:超文本傳輸協議,HTTP鏈接最顯著的特色是客戶端發送的每次請求都須要服務器回送響應,在請求結束後,會主動釋放鏈接。從創建鏈接到關閉鏈接的過程稱爲「一次鏈接」。

5.2 HTTPS 簡介

HTTPS(Secure Hypertext Transfer Protocol)安全超文本傳輸協議 它是一個安全通訊通道 HTTPS是HTTP over SSL/TLS,HTTP是應用層協議,TCP是傳輸層協議,在應用層和傳輸層之間,增長了一個安全套接層SSL/TLS:

  • SSL (Secure Socket Layer,安全套接字層)

  • TLS (Transport Layer Security,傳輸層安全協議)

  • SSL使用40 位關鍵字做爲RC4流加密算法

  • 做用:

A. 內容加密 創建一個信息安全通道,來保證數據傳輸的安全; B. 身份認證 確認網站的真實性 C. 數據完整性 防止內容被第三方冒充或者篡改

5.3 HTTPS 和HTTP 區別

對比項 HTTP HTTPS
是否須要到CA申請證書 不須要 須要
默認端口號 80 443
信息是否加密 超文本傳輸協議,信息是明文傳輸 是具備安全性的ssl加密傳輸協議
是否有狀態 是無狀態的 HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

5.4 HTTP狀態碼

  • 2開頭 (請求成功)表示成功處理了請求的狀態代碼。
HTTP協議狀態碼 意義 說明
200 成功 服務器已成功處理了請求. 一般,這表示服務器提供了請求的網頁
201 已建立 請求成功而且服務器建立了新的資源
202 已接受 服務器已接受請求,但還沒有處理
203 非受權信息 服務器已成功處理了請求,但返回的信息可能來自另外一來源
204 無內容 服務器成功處理了請求,但沒有返回任何內容
205 重置內容 服務器成功處理了請求,但沒有返回任何內容
206 部份內容 服務器成功處理了部分 GET 請求
  • 3開頭 (請求被重定向)表示要完成請求,須要進一步操做。 一般,這些狀態代碼用來重定向。
HTTP協議狀態碼 意義 說明
300 多種選擇 針對請求,服務器可執行多種操做。 服務器可根據請求者 (user agent) 選擇一項操做,或提供操做列表供請求者選擇
301 永久移動 請求的網頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置
302 臨時移動 服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來進行之後的請求
303 查看其餘位置 請求者應當對不一樣的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼
304 未修改 自從上次請求後,請求的網頁未修改過。 服務器返回此響應時,不會返回網頁內容
305 使用代理 請求者只能使用代理訪問請求的網頁。 若是服務器返回此響應,還表示請求者應使用代理
307 臨時重定向 服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來進行之後的請求
  • 4開頭 (請求錯誤)這些狀態代碼表示請求可能出錯,妨礙了服務器的處理。
HTTP協議狀態碼 意義 說明
400 錯誤請求 服務器不理解請求的語法
401 未受權 請求要求身份驗證。 對於須要登陸的網頁,服務器可能返回此響應
402
403 禁止 服務器拒絕請求
404 未找到 服務器找不到請求的網頁
405 方法禁用 禁用請求中指定的方法
406 不接受 沒法使用請求的內容特性響應請求的網頁
407 須要代理受權 此狀態代碼與 401(未受權)相似,但指定請求者應當受權使用代理
408 請求超時 服務器等候請求時發生超時
409 衝突 服務器在完成請求時發生衝突。 服務器必須在響應中包含有關衝突的信息
410 已刪除 若是請求的資源已永久刪除,服務器就會返回此響應
411 須要有效長度 服務器不接受不含有效內容長度標頭字段的請求。 (報文不一致)
412 未知足前提條件 服務器未知足請求者在請求中設置的其中一個前提條件
413 請求實體過大 服務器沒法處理請求,由於請求實體過大,超出服務器的處理能力
414 請求的 URI 過長 請求的 URI(一般爲網址)過長,服務器沒法處理
415 不支持的媒體類型 請求的格式不受請求頁面的支持
416 請求範圍不符合要求 若是頁面沒法提供請求的範圍,則服務器會返回此狀態代碼
417 未知足指望值 服務器未知足"指望"請求標頭字段的要求
  • 5開頭(服務器錯誤)這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤多是服務器自己的錯誤,而不是請求出錯。
HTTP協議狀態碼 意義 說明
500 服務器內部錯誤 服務器遇到錯誤,沒法完成請求
501 還沒有實施 服務器不具有完成請求的功能。 例如,服務器沒法識別請求方法時可能會返回此代碼
502 錯誤網關 服務器做爲網關或代理,從上游服務器收到無效響應
503 服務不可用 服務器目前沒法使用(因爲超載或停機維護)。 一般,這只是暫時狀態
504 網關超時 服務器做爲網關或代理,可是沒有及時從上游服務器收到請求
505 HTTP 版本不受支持 服務器不支持請求中所用的 HTTP 協議版本

6. socket

6.1 socket 簡介

socket是爲了實現以上的通訊過程而創建成來的通訊管道,其真實的表明是客戶端和服務器端的一個通訊進程,雙方進程經過socket進行通訊,而通訊的規則採用指定的協議。 socket只是一種鏈接模式,不是協議,socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。tcp、udp,簡單的說(雖然不許確)是兩個最基本的協議,不少其它協議都是基於這兩個協議如,http就是基於tcp的,.用socket能夠建立tcp鏈接,也能夠建立udp鏈接,這意味着,用socket能夠建立任何協議的鏈接,由於其它協議都是基於此的。

6.2 socket 特色

socket 優勢 缺點
socket 傳輸數據爲字節級,傳輸數據可自定義,數據量小(對於手機應用講:費用低) 需對傳輸的數據進行解析,轉化成應用級的數據
socket 傳輸數據時間短,性能高 對開發人員的開發水平要求高
socket 適合於客戶端和服務器端之間信息實時交互 相對於Http協議傳輸,增長了開發量
socket 能夠加密,數據安全性強

6.3 socket 對比 HTTP

6.4 socket 創建鏈接過程

socket 創建鏈接過程

  • 在前面已經講解過了TCP的三次握手創建鏈接,四次揮手斷開鏈接的過程。 這裏socket的創建鏈接和斷開鏈接起始就是這個過程的代碼實現。

  • 回顧tcp三次握手過程

    1. 第一次握手:客戶端嘗試鏈接服務器,向服務器發送syn包,syn=j,客戶端進入SYN_SEND狀態等待服務器確認

    2. 第二次握手:服務器接收客戶端syn包並確認(ack=j+1),同時向客戶端發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態

    3. 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手

流程圖:

TCP三次握手

  • 根據tcp的三次握手,socket也定義了三次握手,也許是參考tcp的三次握手,一些計算機大神們畫出了socket的三次握手的模型圖
    socket三次握手

socket函數實現三次握手

  • socket創建鏈接客戶端流程

(1)打開一通訊通道,並鏈接到服務器所在主機的特定端口; (2)向服務器發服務請求報文,等待並接收應答;繼續提出請求...... (3)請求結束後關閉通訊通道並終止。

  • socket創建鏈接服務器端流程

(1)打開一通訊通道並告知本地主機,它願意在某一公認地址上的某端口(如FTP的端口可能爲21)接收客戶請求; (2)等待客戶請求到達該端口; (3)接收到客戶端的服務請求時,處理該請求併發送應答信號。接收到併發服務請求,要激活一新進程來處理這個客戶請求(如UNIX系統中用fork、exec)。新進程處理此客戶請求,並不須要對其它請求做出應答。服務完成後,關閉此新進程與客戶的通訊鏈路,並終止。 (4)返回第(2)步,等待另外一客戶請求。 (5)關閉服務器

6.5 socket 創建鏈接代碼實現

6.5.1 客戶端代碼

  • swift代碼
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        // 處理耗時操做的代碼塊...
        
    
    // 建立socket
    /* 1.AF_INET: ipv4 執行ip協議的版本 2.SOCK_STREAM:指定Socket類型,面向鏈接的流式socket 傳輸層的協議 3.IPPROTO_TCP:指定協議。 IPPROTO_TCP 傳輸方式TCP傳輸協議 返回值 大於0 建立成功 */
    _clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    // 創建鏈接(與服務器)
    /* 終端裏面 命令模擬服務器 netcat nc -lk 12345 參數一:套接字描述符 參數二:指向數據結構sockaddr的指針,其中包括目的端口和IP地址 參數三:參數二sockaddr的長度,能夠經過sizeof(struct sockaddr)得到 返回值 int -1失敗 0 成功 */
    struct sockaddr_in addr; /* 填寫sockaddr_in結構*/ addr.sin_family = AF_INET; addr.sin_port=htons(8080); addr.sin_addr.s_addr = inet_addr("192.168.0.99"); int connectResult = connect( _clientSocket, (const struct sockaddr *)&addr, sizeof(addr)); // 發送數據(到服務器) /* 第一個參數指定發送端套接字描述符; 第二個參數指明一個存放應用程式要發送數據的緩衝區; 第三個參數指明實際要發送的數據的字符數; 第四個參數通常置0。 成功則返回實際傳送出去的字符數,失敗返回-1, */ char * str = "itcast"; ssize_t sendLen = send( _clientSocket, str, strlen(str), 0); // 接送數據(從服務器) /* 第一個參數socket 第二個參數存放數據的緩衝區 第三個參數緩衝區長度。 第四個參數指定調用方式,通常置0 返回值 接收成功的字符數 */ char *buf[1024]; ssize_t recvLen = recv( _clientSocket, buf, sizeof(buf), 0); NSLog(@"---->%ld",recvLen); }); // [self test]; } 複製代碼
  • c 語言實現代碼
#include<stdio.h> 
#include<stdlib.h> 
#include<netinet/in.h> 
#include<sys/socket.h> 
#include<arpa/inet.h> 
#include<string.h> 
#include<unistd.h> 
#define BUFFER_SIZE 1024 
  
int main(int argc, const char * argv[]) {  
    struct sockaddr_in server_addr;  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(11332);  
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  
    bzero(&(server_addr.sin_zero), 8);  
  
    int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);  
    if(server_sock_fd == -1)  
    {  
    perror("socket error");  
    return 1;  
    }  
    char recv_msg[BUFFER_SIZE];  
    char input_msg[BUFFER_SIZE];  
  
    if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == 0)  
    {  
    fd_set client_fd_set;  
    struct timeval tv;  
  
    while(1)  
    {  
        tv.tv_sec = 20;  
        tv.tv_usec = 0;  
        FD_ZERO(&client_fd_set);  
        FD_SET(STDIN_FILENO, &client_fd_set);  
        FD_SET(server_sock_fd, &client_fd_set);  
  
       select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv);  
        if(FD_ISSET(STDIN_FILENO, &client_fd_set))  
        {  
            bzero(input_msg, BUFFER_SIZE);  
            fgets(input_msg, BUFFER_SIZE, stdin);  
            if(send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1)  
            {  
                perror("發送消息出錯!\n");  
            }  
        }  
        if(FD_ISSET(server_sock_fd, &client_fd_set))  
        {  
            bzero(recv_msg, BUFFER_SIZE);  
            long byte_num = recv(server_sock_fd, recv_msg, BUFFER_SIZE, 0);  
            if(byte_num > 0)  
            {  
            if(byte_num > BUFFER_SIZE)  
            {  
                byte_num = BUFFER_SIZE;  
            }  
            recv_msg[byte_num] = '\0';  
            printf("服務器:%s\n", recv_msg);  
            }  
            else if(byte_num < 0)  
            {  
            printf("接受消息出錯!\n");  
            }  
            else  
            {  
            printf("服務器端退出!\n");  
            exit(0);  
            }  
        }  
        }  
    //} 
    }  
    return 0;  
} 


複製代碼

6.5.2 服務器端代碼

#include<stdio.h>  
#include<stdlib.h>  
#include<netinet/in.h>  
#include<sys/socket.h>  
#include<arpa/inet.h>  
#include<string.h>  
#include<unistd.h>  
#define BACKLOG 5     //完成三次握手但沒有accept的隊列的長度 
#define CONCURRENT_MAX 8   //應用層同時能夠處理的鏈接 
#define SERVER_PORT 11332  
#define BUFFER_SIZE 1024  
#define QUIT_CMD ".quit"  
int client_fds[CONCURRENT_MAX];  
int main(int argc, const char * argv[])  
{  
    char input_msg[BUFFER_SIZE];  
    char recv_msg[BUFFER_SIZE];  
    //本地地址 
    struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero), 8); //建立socket int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_sock_fd == -1) {  
        perror("socket error");  
        return 1;  
    }  
    //綁定socket 
    int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(bind_result == -1) {  
        perror("bind error");  
        return 1;  
    }  
    //listen 
    if(listen(server_sock_fd, BACKLOG) == -1)  
    {  
        perror("listen error");  
        return 1;  
    }  
    //fd_set 
    fd_set server_fd_set;  
    int max_fd = -1;  
    struct timeval tv; //超時時間設置 while(1) {  
        tv.tv_sec = 20;  
        tv.tv_usec = 0;  
        FD_ZERO(&server_fd_set);  
        FD_SET(STDIN_FILENO, &server_fd_set);  
        if(max_fd <STDIN_FILENO)  
        {  
            max_fd = STDIN_FILENO;  
        }  
        //printf("STDIN_FILENO=%d\n", STDIN_FILENO); 
    //服務器端socket 
        FD_SET(server_sock_fd, &server_fd_set);  
       // printf("server_sock_fd=%d\n", server_sock_fd); 
        if(max_fd < server_sock_fd)  
        {  
            max_fd = server_sock_fd;  
        }  
    //客戶端鏈接 
        for(int i =0; i < CONCURRENT_MAX; i++)  
        {  
            //printf("client_fds[%d]=%d\n", i, client_fds[i]); 
            if(client_fds[i] != 0)  
            {  
                FD_SET(client_fds[i], &server_fd_set);  
                if(max_fd < client_fds[i])  
                {  
                    max_fd = client_fds[i];  
                }  
            }  
        }  
        int ret = select(max_fd + 1, &server_fd_set, NULL, NULL, &tv);  
        if(ret < 0)  
        {  
            perror("select 出錯\n");  
            continue;  
        }  
        else if(ret == 0)  
        {  
            printf("select 超時\n");  
            continue;  
        }  
        else  
        {  
            //ret 爲未狀態發生變化的文件描述符的個數 
            if(FD_ISSET(STDIN_FILENO, &server_fd_set))  
            {  
                printf("發送消息:\n");  
                bzero(input_msg, BUFFER_SIZE);  
                fgets(input_msg, BUFFER_SIZE, stdin);  
                //輸入「.quit"則退出服務器 
                if(strcmp(input_msg, QUIT_CMD) == 0)  
                {  
                    exit(0);  
                }  
                for(int i = 0; i < CONCURRENT_MAX; i++)  
                {  
                    if(client_fds[i] != 0)  
                    {  
                        printf("client_fds[%d]=%d\n", i, client_fds[i]);  
                        send(client_fds[i], input_msg, BUFFER_SIZE, 0);  
                    }  
                }  
            }  
            if(FD_ISSET(server_sock_fd, &server_fd_set))  
            {  
                //有新的鏈接請求 
                struct sockaddr_in client_address; socklen_t address_len; int client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &address_len); printf("new connection client_sock_fd = %d\n", client_sock_fd); if(client_sock_fd > 0) {  
                    int index = -1;  
                    for(int i = 0; i < CONCURRENT_MAX; i++)  
                    {  
                        if(client_fds[i] == 0)  
                        {  
                            index = i;  
                            client_fds[i] = client_sock_fd;  
                            break;  
                        }  
                    }  
                    if(index >= 0)  
                    {  
                        printf("新客戶端(%d)加入成功 %s:%d\n", index, inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));  
                    }  
                    else  
                    {  
                        bzero(input_msg, BUFFER_SIZE);  
                        strcpy(input_msg, "服務器加入的客戶端數達到最大值,沒法加入!\n");  
                        send(client_sock_fd, input_msg, BUFFER_SIZE, 0);  
                        printf("客戶端鏈接數達到最大值,新客戶端加入失敗 %s:%d\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));  
                    }  
                }  
            }  
            for(int i =0; i < CONCURRENT_MAX; i++)  
            {  
                if(client_fds[i] !=0)  
                {  
                    if(FD_ISSET(client_fds[i], &server_fd_set))  
                    {  
                        //處理某個客戶端過來的消息 
                        bzero(recv_msg, BUFFER_SIZE);  
                        long byte_num = recv(client_fds[i], recv_msg, BUFFER_SIZE, 0);  
                        if (byte_num > 0)  
                        {  
                            if(byte_num > BUFFER_SIZE)  
                            {  
                                byte_num = BUFFER_SIZE;  
                            }  
                            recv_msg[byte_num] = '\0';  
                            printf("客戶端(%d):%s\n", i, recv_msg);  
                        }  
                        else if(byte_num < 0)  
                        {  
                            printf("從客戶端(%d)接受消息出錯.\n", i);  
                        }  
                        else  
                        {  
                            FD_CLR(client_fds[i], &server_fd_set);  
                            client_fds[i] = 0;  
                            printf("客戶端(%d)退出了\n", i);  
                        }  
                    }  
                }  
            }  
        }  
    }  
    return 0;  
} 


複製代碼

7. 端口

7.1.端口簡介

經常使用默認端口號 網絡層---數據包的包格式裏面有個很重要的字段叫作協議號。好比在傳輸層若是是TCP鏈接那麼在網絡層IP包裏面的協議號就將會有個值是6若是是UDP的話那個值就是17---傳輸層。

  • 傳輸層---經過接口關聯(端口的字段叫作端口)---應用層。
  • 用netstat –an 能夠查看本機開放的端口號。

7.2. 服務器經常使用接口

  • 代理服務器經常使用如下端口:
服務器協議 默認端口 說明
HTTP協議 80/8080/3128/8081/9080
SOCKS 1080
FTP文件傳輸協議 21
Telnet遠程登陸協議 23
HTTP服務器 默認的端口號爲80/tcp木馬Executor開放此端口
HTTPSsecurely transferring web pages服務器 默認的端口號爲443/tcp 443/udp
Telnet不安全的文本傳送 默認端口號爲23/tcp木馬Tiny Telnet Server所開放的端口
FTP 默認的端口號爲21/tcp木馬Doly Trojan、Fore、InvisibleFTP、WebEx、WinCrash和Blade Runner所開放的端口
TFTP (Trivial File Transfer Protocol) 默認的端口號爲69/udp
SSH安全登陸、SCP文件傳輸、端口重定向 默認的端口號爲22/tcp
SMTP Simple Mail Transfer Protocol (E-mail) 默認的端口號爲25/tcp木馬Antigen、EmailPassword Sender、Haebu Coceda、ShtrilitzStealth、WinPC、WinSpy都開放這個端口
POP3 Post Office Protocol (E-mail) 默認的端口號爲110/tcp
WebLogic 默認的端口號爲7001
Webshpere應用程序 默認的端口號爲9080
webshpere管理工具 默認的端口號爲9090
JBOSS 默認的端口號爲8080
TOMCAT 默認的端口號爲8080
WIN2003遠程登錄 默認的端口號爲3389
Symantec AV/Filter for MSE 默認端口號爲8081 Oracle 數據庫默認的端口號爲1521
ORACLE EMCTL 默認的端口號爲1158
Oracle XDB XML 數據庫默認的端口號爲8080
Oracle XDB FTP服務 默認的端口號爲2100
MS SQL*SERVER數據庫server 默認的端口號爲1433/tcp 1433/udp
MS SQL*SERVER數據庫monitor 默認的端口號爲1434/tcp 1434/udp
QQ 默認的端口號爲1080/udp

8. webRTC

詳情參考我以前的博客:webRTC簡介

9. RTMP/RTSP協議

詳情參考我以前的博客:

  1. RTMP相關知識
  2. RTSP相關知識

網絡抓包工具

詳情參考我以前的博客: 網絡抓包工具

alamofire框架

1. alamofire框架簡介

alamofire框架簡介

相關文章
相關標籤/搜索