收發資料的原理(上)

Dwyane_Coding發表於2018-12-06

前言:網路知識非常的重要,如果你不是做程式的,那麼一些網路常識還是得知道的;而做程式的,就更不用說了,不僅需要了解一些網路知識,還是知道其原理,如果不瞭解原理,不敢說他不是程式設計師,但是總缺了點意思,就像去北京沒去過長城一樣。

網路原理系列文章:

一、五分鐘瞭解網路連線(已完成)

二、收發資料的原理(上)(已完成)

三、收發資料的原理(下)(已完成)

四、收發資料的番外篇(未完成)

上一篇五分鐘瞭解網路連線講了網路連線的大概流程,並且文末講到客戶端委託協議棧收發資料可以總結為四步:

1、建立套接字(建立套接字階段)

2、將管道連線到伺服器端的套接字上(連線階段)

3、收發資料(通訊階段)

4、斷開管道並刪除套接字(斷開階段)

本文會對前兩個步驟展開描述,後面兩個步驟,下一篇文章介紹,敬請關注!由於網路知識點非常繁雜,讀者請沉住氣閱讀此文,我儘可能詳細介紹,儘可能的不那麼枯燥,所以本文介紹也有側重點。讀完本文,你可能會對一些知識恍然大悟,哦,原來是這樣啊!好了,廢話不多說,直接進入主題。

創造套接字

協議棧的內部結構

image.png

上面是協議棧的內部結構。分為接個部分。上面部分會向下面的部分委派工作。

應用程式的下面是Socket庫,其中包括解析器,解析器向DNS伺服器發出查詢,它的工作過程我們在上一篇已經介紹過了。

再下面就是作業系統內部了,其中包括協議棧。協議棧的上半部分有兩塊,分別是負責用TCP協議收發資料的部分和負責用UDP協議收發資料的部分,它們會接受應用程式的委託執行收發資料的操作。關於TCP和UDP,會在後面文章講解,目前只要知道像瀏覽器、郵件等一般的應用程式、QQ檔案傳輸都是使用TCP收發資料的,而像DNS查詢、QQ語音 、QQ視訊等收發較短的控制資料的時候則使用UDP。

協議棧的下半部分是利用IP協議控制網路包收發資料的部分,在網際網路中傳送資料,需要將資料分成一個個小的網路包,然後將網路包傳送給通訊物件就是由IP負責的。另外,ICMP用於錯誤提示以及各種控制訊息,ARP則是用於查詢IP相應的乙太網MAC地址。現在只需要大概知道這個名詞,後面才會細講。

IP下面是驅動程式負責控制網路卡硬體,最下面的網路卡則是負責完成實際的收發操作——對網線中的訊號執行傳送和接受操作。

套接字的實體

實際上套接字並沒存在實體,只是一個概念。在協議棧內部有一塊存放控制資訊的記憶體空間,用於記錄通訊操作的控制資訊。比如通訊物件的IP地址、埠號、通訊操作的狀態等。所以硬要說套接字是個實體,那就是這些控制資訊或者是儲存這些控制資訊的記憶體空間。

協議棧執行操作時需要參閱這些控制資訊。來決定下一步該做什麼。比如:傳送資料時,看看IP地址和埠號;傳送資料後,協議棧需要等待對方返回資料的響應資訊,但是資料可能會半途丟失。我們不可能一直等待,所以套接字中需要記錄是否已經收到或者傳送資料了多久,才方便知道是否要重發資料。套接字的控制資訊還有很多作用,在此不一一列舉了。

協議棧是根據套接字中記錄的控制資訊工作的。

呼叫Socket庫中的元件

建立套接字時,需要呼叫Socket庫中的socket元件,注意這裡,大寫的是Socket,小寫是庫中的一個程式元件。

應用程式呼叫socket程式申請建立套接字,而協議棧則根據應用程式的申請執行建立套接字的操作。

在建立過程中,協議棧會分配一個用於存放套接字所需的記憶體空間,用於存放記錄套接字操作的控制資訊。建立套接字時,資料收發操作還沒開始,所以把這初始狀態的資訊存入記憶體空間中。到此,建立套接字操作完成。

建立套接字,不僅要分配空間,而且需要初始化狀態資訊。

然後,套接字需要將它的描述符告訴應用程式。描述符相當於車庫號,告訴我車庫號,我才知道哪個才是我要的車庫。同樣,描述符是用在應用程式委託協議棧收發資料的時候。套接字包含了通訊物件的資訊,比如已經說過的IP地址、埠號,所以應用程式收到套接字的描述符,應用程式再提供給協議棧,協議棧就知道了套接字中所包含的通訊物件資訊,就可以準備連線通訊物件了。

連線伺服器

關於連線

因為歷史原因,其實這裡的“連線”就更像是通訊前的準備,叫“準備”其實更適合

呼叫socket程式申請建立完套接字,然後應用程式會呼叫Socket庫的另外一個程式元件connect,這樣協議棧就會將本地的套接字與伺服器的套接字進行連線。這裡的連線是指通訊雙方交換控制資訊,在套接字記錄一些必要資訊並準備資料收發的一連串操作。

我們說的連線不是指網線一直插著的連線,不是指通訊過程中將資料轉換成電訊號。而是當應用程式委託傳送資料時,協議棧通過描述符找到的套接字取得通訊物件的IP地址和埠號等資訊。這屬於連線操作的目的之一。

說完應用程式,再說下伺服器那邊,伺服器也會建立套接字,但是伺服器的協議棧和客戶端這邊一樣,沒有類似一個描述符的東西就沒辦法知道通訊物件,沒法開始通訊。所以得有客戶端先開始請求,告訴伺服器必要資訊。比如“我要和你請求,我的IP地址是10.10.1.118,埠號是8900”。所以,應用程式向伺服器傳送請求,也是連線操作的目的之一。

連線實際上通訊雙方交換控制資訊,在套接字中記錄必要資訊並準備資料收發的一連串操作。

控制資訊,是控制資料收發操作的一些資訊。IP地址、埠號就屬於其中的資訊。其餘的控制資訊,我們後面再介紹。雙方通過通訊規則進行資訊交換從而完成資料收發準備。收發操作,需要一塊臨時存放要收發的資料的記憶體空間,這塊記憶體空間叫做緩衝區,它是在連線操作過程中分配的。

關於控制資訊頭部

控制資訊可以分為兩類。一是客戶端和伺服器相互聯絡時交換的控制資訊。二是儲存在套接字中,用來控制協議棧操作的資訊。

第一類:客戶端和伺服器交換的控制資訊,不僅是在連線時需要,包括資料收發和斷開連線操作在內,整個通訊過程都需要。我們前面說過,資料會被分成一個個網路包傳送,而這些資訊就是被新增在客戶端與伺服器傳遞的網路包的開頭。在連線階段,由於資料收發還沒有開始,網路包中沒有實際資料,只有控制資訊。這些控制資訊位於網路包的開頭,因此成為頭部。當然,乙太網和IP協議也有自己的頭部(包含著控制資訊),為了避免不同頭部混淆,我們一般記作TCP頭部,乙太網頭部、IP頭部。本文主要講解TCP頭部,其餘知識後面再講,讀者有興趣可先自行查閱。

客戶端和伺服器在通訊中會將必要的資訊存放在頭部並相互確認。大家現在要知道的就是頭部是用來記錄和交換控制資訊的。

第二類:套接字中的控制資訊。這裡會儲存應用程式傳遞來的資訊以及從通訊物件接收到的資訊,還有收發資料操作的執行狀態等資訊也會儲存於此,協議棧根據這些資訊來執行每一步操作。

通訊操作中使用的控制資訊分為兩類: (1)頭部中的資訊 (2)套接字(協議棧的記憶體空間)中記錄的資訊

連線操作的實際過程

上面我們已經介紹了連線(準備)操作的含義,接下來看一下具體操作過程。首先是應用程式呼叫Socket庫中的connect,類似下面?

connect (<描述符>,<伺服器IP地址>,<伺服器埠號>,...)

上面的呼叫提供了伺服器的IP地址和埠號,這些資訊會傳遞給協議棧中的TCP模組。TCP模組就會與該IP地址對應的物件,也就是與伺服器的TCP模組交換控制資訊。

tcp-header

seq:(Sequence Number):本報文段資料的第一個位元組的序號 ack:(Acknowledgment Number):確認號——期望收到對方下個報文段的第一個資料位元組的序號

控制位包含以下幾部分 URG:緊急指標有效位。 SYN:建立連線,當需要建立連線時,他的值為1.即SYN=1 ACK:確認連線,當ACK=1是才有效,ACK=0是此控制位無效。 FIN:斷開連線,提出斷開連線這一方的值為1. RST:重新建立連線,值為1時代表重新建立連線。 PSH:要求接收方將資料儘快將資料段送達應用層

上圖主要介紹了TCP頭部。其中TCP頭部目前要認知的有傳送方和接收方的埠號,序號(seq)是傳送方告訴接收方本報文段資料的第一個位元組的序號,ack號是接收方告訴傳送方已收到了所有資料的第幾個位元組。其中ack是Acknowledgment Number的縮寫。可能讀者納悶IP地址在哪,IP地址其實在IP頭部才有。由於本文重點是介紹TCP,所以下方只給出IP頭部圖,讀者自行閱讀

ip-header

應用程式與伺服器的TCP模組交換控制資訊這一過程包含幾個步驟:首先,客戶端先建立一個包含表示開始資料收發操作的控制資訊的頭部(上面所說網路包中開頭的控制資訊),頭部包含很多欄位,其中要關注的重點是客戶端和伺服器的埠號,也就是說,客戶端的套接字知道了連線伺服器的哪個套接字。然後,我們把頭部中的控制位的SYN設定為1,大家可以認為它表示連線。此外,還需設定適當的序號和視窗大小。

連線操作的第一步是在TCP模組處建立表示連線控制資訊的頭部

通過TCP頭部中的傳送方和接收方的埠號可以找到要連線的套接字

當TCP頭部建立好之後,接下來TCP模組會將資訊傳給IP模組並委託它進行傳送。IP模組執行網路包傳送操作後,網路包就會通過網路傳送到伺服器的IP模組,再由伺服器的IP模組把接收到的資料傳給伺服器自身的TCP模組,這時,伺服器的TCP模組會根據TCP頭部的資訊找到埠號對應的套接字,然後套接字就會寫入相應的資訊,並把狀態改成正在連線。

TCP模組、IP模組分別屬於網路原理中OSI模型7層結構的傳輸層、網路層,而傳輸層處於網路層的上一層,也就是高一層,要完成傳送資料,必須從通訊一方的高層傳到低層,再通過網路傳給通訊另外一方的低層,再到那一方的高層完成接收。所以傳送資料得從高一層的TCP到低層的IP模組逐一傳遞。

上述操作完成後,伺服器的TCP模組會返回響應,這個過程跟客戶端傳送資料給服務端一樣,需要在TCP頭部中設定傳送方和接收方埠以及SYN位元。另外,客戶端向伺服器傳送第一個網路包時,由於伺服器還沒有接受過網路包,所以ACK位元設為0,那麼在返回響應就需要將ACK控制位設為1,表示已經收到相應的網路包。網路中經常發生錯誤,網路包也會丟失。因此通訊雙方必須相互確認網路包是否已經送達,通訊雙方如何確認?其實就是通過設定ACK。接下來,伺服器TCP模組會講TCP頭部傳給IP模組,並委託IP模組向客戶端返回響應。

然後,網路包就會返回客戶端,通過IP模組到達TCP模組,並通過TCP頭部資訊確認連線伺服器的操作是否成功。如果SYN為1,則表示連線成功,這時會向套接字中寫入伺服器的IP地址、埠號等資訊,同時還會將狀態改成連線完畢。到這裡,客戶端的操作就已經完成。但其實還剩下一個步驟,客戶端收到資料後,也要像伺服器那樣把把ACK設定為1,併發回給伺服器,告訴伺服器,我已經收到伺服器發來的響應包,當伺服器收到這個返回包後,連線操作才算全部完成。

有沒有覺得上面的雙方相互確認網路包操作,似乎很熟悉,沒錯!它其實就是tcp的三次握手。

TCP三次握手 1.A向B發起建立連線請求: 2.B收到A的傳送訊號,並且向A傳送確認資訊 3.A收到B的確認訊號,並且向B傳送確認訊號

連線(準備)操作完成後,套接字可以隨時進行收發資料了,這個時候我們可以理解通訊雙方已經有一條相連的管道,這條管道連線著雙方的套接字。當然這條管道並不真正存在,只是業界為了方便理解,比喻而已。

建立連線後,協議棧的連線操作就結束了。也就是說,當初應用程式呼叫Socket庫中connect程式元件操作已經執行完畢,控制流程又重新交回到客戶端。等到後面的收發資料操作。

在此,收發資料的建立套接字階段、連線階段已經講完,剩下的通訊階段、斷開階段留到下次再講。網路的東西很枯燥,並且並不是那麼視覺化,學會的要訣是沉住氣,看到這裡,如果你現在還不能講到收發資料的前兩個步驟,請回到文章頂部,再看一遍。Over and over again,你就會學有所成。




歡迎關注技術公眾號「程式設計師大咖秀」

相關文章