DNS是一個與HTTP同樣位於應用層的協議,它提供了域名轉IP地址的解析服務(1)
不過問題來了,既然電腦(計算機)可以同時擁有域名與IP地址,何不使用域名來表示?同理為何我們輸入網址時不直接使用IP作為輸入?
TCP/IP為了識別網路中的主機,會分配唯一的IP地址給主機,再依據IP地址來進行通訊,但由於真的很難記住,於是TCP/IP協議就利用主機名幫每一台電腦命名,也就是域名系統
其實我們可以想像使用域名地址與IP地址的兩種場景,假設今天我們手上沒有任何記事工具,單純靠記憶記住該網站的訪問地址,哪一種地址解釋方法更為直觀、好懂?
可能有人不服,這種簡單的數學排列組合那可是輕而易舉,背個100條也是小菜一疊,不過以面向使用者的,或者單就效率來說,利用最節省資源的方法,使更多人能夠理解與運用才是更好的方法。既然如此何不讓看得懂文字的使用者使用域名系統,讓腦子裡只有01的電腦使用IP地址呢?
你可以把DNS想像成是一個儲存域名地址與IP地址對應關係的資料庫,電腦會向DNS詢問這個域名對應的IP地址是多少
從圖中我們可以看到DNS服務器的工作就是返回請求的IP地址給主機,每台主機中都會存儲預設的DNS服務器地址,當主機想要查詢IP地址時,都會委託它進行查找。不過問題來了,世界上的網站是何其多,一個服務器是沒有辦法一次儲存那麼多條地址訊息的,到底DNS服務器們是怎麼達成這個任務的?
真實的網路世界往往存在無數多台服務器,而且每一個域名都是不可重複的,想讓每一台DNS服務器記下所有域名對應的IP地址是不切實際的,因此工程師們想出一個辦法,何不讓DNS服務器們合力記住所有對應地址?
因此設計者使用分布式分層架構來設計整個DNS系統,讓下級的DNS服務器的IP註冊到它們的上級,上級服務器再註冊IP到它們的更上級,從而使我們可以從最高級別DNS服務器完成域名查詢任務
當主機端發起請求時,DNS服務器就可以直接回復儲存的地址,或者可以向主機端提供更低階級同時也更仔細的DNS服務器地址,讓主機向指定DNS服務器發起請求,如此環環相扣的連續收發,最終可以取得對應的IP地址
DNS服務器的分層架構如上圖所示,還記得我們的電腦會儲存離主機最近的DNS服務器地址嗎,首先該DNS服務器會向root(根域服務器)(2)發起請求,而根域服務器,你可以想像成一切的開端,它儲存了多個頂級網域服務器地址,因此透過根域服務器可以率先訪問儲存最高等級域名的DNS服務器,以此類推,當到達第一層級域名服務器時,我們會查詢它儲存的第二級域名,有點像偵探辦案一樣一個線索一個線索找出最終答案
另外我們也發現既然層次最高的是國家或地區頂級域,但是有些網站並沒有填入阿,依然是.com或.edu結尾,這到底是怎麼一回事?其實.com是代表國際域名,而.com.tw則是台灣域名,它們都代表著第一層級域名,差別只在於搜尋的優先度,假若我們使用.com.tw那麼網頁搜尋就會以台灣範圍為優先
我們從DNS原理框架圖可以看出,主機端會讓它儲存的預設DNS服務器代替他來進行DNS域名查找,而我們也知道所有服務器都擁有根域服務器地址,有了根域地址就可以展開一系列的尋址動作啦
第一步讓我們來看看域名吧,URL的最右側域名區段是.com.tw(最高級別),該服務器可能儲存的資料如下表:
域名 | IP地址 | Class | Type |
---|---|---|---|
myblog.com.tw | 192.168.0.1 | IN | A |
herblog.com.tw | 192.168.0.2 | IN | A |
hisblog.com.tw | 192.168.0.3 | IN | A |
www.haha.com.tw | 192.168.0.4 | IN | A |
其中域名列就是該服務器上總共儲存幾條域名消息,我們可以看到域名消息不一定通通都是同樣類型的,它可能有不同等級的域名,或者不同的儲存類型(A代表域名對應的響應數據是IP地址),而Class In代表Internet
有了! myblog.com.tw這條域名最接近我們的請求域名,於是DNS服務器將它代表的IP地址返回給預設DNS服務器,好讓它向下一台DNS服務器發起請求,這一系列動作會直到被訪問的DNS服務器確實擁有完整的請求域名為止,我們用找人問事來比喻DNS的查找行為
由此可知預設的DNS服務器會代替主機從最高級別一步步搜尋完整的域名地址,當被訪問DNS服務器在接收到請求時,會查找保存的資料表,並把它所知道的最接近域名的IP地址回報出去
一旦預設的DNS服務器在某台DNS服務器上找到儲存完整域名的服務器地址時(這時它訪問到的只是儲存該條域名的服務器地址),將會主動將該服務器IP地址告知主機端,讓主機端與這台服務器直接建立溝通,以獲取最終的IP地址
Resolver又名解析器,是DNS查找原理的實現,因為應用層不負責連線的工作,因此必須仰賴OS調用Socket函式庫來進行操作,解析器其實就是Socket函式庫中的一個function API,我們在上面也介紹了,主機端向DNS服務器發起請求,發起請求就代表一定存在一對服務器端與客戶端,解析器就擔當主機上的DNS客戶端角色
#include <netdb.h>
#include <sys/socket.h>
...
address = gethostbyname("www.myblog.com..tw"); //解析器
...
上圖簡單的說明了應用程式調用了 socket庫函式中的gethostbyname()(3)後會發生的事。首先函式會根據DNS服務器的類型規格生成特定的DNS請求(二進制形式)接下來OS會委託*Protocol Stack(協議棧或協議疊)(4)來執行DNS請求的發送(5),該請求會透過網卡確實傳到DNS手裡,這裡剛好沿續上一小節所講的尋找指定DNS服務器,存儲對應IP位置的DNS服務器會將響應結果透過網卡回傳給主機端,經過協議棧的提取與處理,最終將IP地址消息回傳給指定變數,既然有了IP地址應用程式就可以考慮發送請求的事啦。
(1):其實DNS也提供IP轉域名的查找服務
(2):根域約儲存13個第一層級IP地址,另外根域服務器地址存在於所有DNS服務器中
(3):目前gethostbyname()已經被淘汰,改而使用功能更強大的getaddrinfo()
(4):協議棧是OS內部關於網路協定的軟體實現
(5):傳輸層使用UDP協議,省去握手機制,假如發生掉包協議棧會立刻在重新發送消息