Android網路框架學習——DNS解析與Socket通訊原則

一、什麼是DNS?

它所提供的服務是用來將主機名和域名轉換為IP地址的工作。我們知道域名和IP地址是一一對應的關係,但是多個域名可以對應同一個IP地址。

Android網路框架學習——DNS解析與Socket通訊原則

二、DNS查詢過程

遞迴:DNS伺服器可使用其自身的資源記錄資訊快取來應答查詢,也可代表請求客戶機來查詢或聯絡其他DNS伺服器,以完全解析該名稱,並隨後將應答返回至客戶機。迭代:客戶機自己也可嘗試l聯絡其他的DNS伺服器來解析名稱。如果客戶機這麼做,它會使用基於伺服器應答的獨立和附加的查詢。1、在瀏覽器中輸入域名,作業系統會先檢查自己本地的hosts檔案是否有這個網址對映關係。2、如果hosts裡沒有對映關係,查詢本地DNS解析器快取。3、如果還是沒有,首先會找TCP/IP引數中設定的首選DNS伺服器。

Android網路框架學習——DNS解析與Socket通訊原則

三、Socket介紹

Socket 的中文翻譯過來就是“套接字”。套接字是什麼,我們先來看看它的英文含義:插座。

Socket 就像一個電話插座,負責連通兩端的電話,進行點對點通訊,讓電話可以進行通訊,埠就像插座上的孔,埠不能同時被其他程序佔用。而我們建立連線就像把插頭插在這個插座上,建立一個 Socket 例項開始監聽後,這個電話插座就時刻監聽著訊息的傳入,誰撥通我這個“IP 地址和埠”,我就接通誰。

實際上,Socket 是在應用層和傳輸層之間的一個抽象層,它把 TCP/IP 層複雜的操作抽象為幾個簡單的介面,供應用層呼叫實現程序在網路中的通訊。Socket 起源於 UNIX,在 UNIX 一切皆檔案的思想下,程序間通訊就被冠名為

檔案描述符(file descriptor)

,Socket 是一種“開啟—讀/寫—關閉”模式的實現,伺服器和客戶端各自維護一個“檔案”,在建立連線開啟後,可以向檔案寫入內容供對方讀取或者讀取對方內容,通訊結束時關閉檔案。

另外我們經常說到的

Socket 所在位置

如下圖:

Android網路框架學習——DNS解析與Socket通訊原則

四、Socket 通訊過程

Socket 保證了不同計算機之間的通訊,也就是網路通訊。對於網站,通訊模型是伺服器與客戶端之間的通訊。兩端都建立了一個 Socket 物件,然後透過 Socket 物件對資料進行傳輸。通常伺服器處於一個無限迴圈,等待客戶端的連線。

一圖勝千言,下面是

面向連線的 TCP 時序圖

Android網路框架學習——DNS解析與Socket通訊原則

五、使用socket進行DNS協議解析

在進行域名解析的時候,解析程式向域名伺服器發起請求,域名伺服器也就是在作業系統網路配置的時候寫進去的那個DNS伺服器地址,或者也有可能是由 ISP提供的自動獲取的,原理都一樣,域名伺服器收到請求後進行處理,首先在本地快取中查詢對應的域名,找到後將IP地址直接返回,找不到就向其它的授權 伺服器請求資料,又可以分為著名的遞迴查詢和非遞迴查詢。

遞迴查詢就是說自始至終都由一臺域名伺服器進行查詢,它在自己這裡找不到的時候會向其它的域名伺服器請求並且獲取資料,然後返回給請求方。

非遞迴查詢是指域名伺服器收到請求後,如果自己有這個域名的資訊就返回,如果沒有就返回其它域名伺服器的指標,請求方再根據這些域名伺服器再發起查詢。

DNS域名有時候會是一個主域名的別名,比如www。baidu。com,它就是 www。a。shifen。com這個域名的別名,在DNS請求傳送過去之後,response裡面會有一個型別為CNAME的Answers項,裡面包 含了主域名的相關資訊(其實也就是主域名的名稱和TTL),在這個應答訊息裡面可能會出現多個域名訊息,比如每個Answers的第一個欄位就是一個域 名,當然為了減少資料包的容量,DNS系統對域名進行了壓縮,同一個域名只會出現一次,其它的時候再出現的話就會用一個DNS指標表示。 比如域名:www。baidu。com在資料包中的表示是

03

77 77 77

05

62 61 69 64 75

03

63 6f 6d

00

粗體的是長度,將域名中的點去掉,用長度來分隔域名,以0結束。DNS允許的長度為0-63個位元組,所以一個8位的長度最高兩位都為0。

而如果此處域名重複出現,信令中便會用DNS指標代替長度,指標為兩個位元組,16位的最位都為1,剩下的14位表示在在整個資料包中的偏移量,當程 序讀取到c00c的時候很容易判斷它是一個指標而不是一個長度欄位,於是根據c00c指向的領移量,即從資料包開始後的第12個位元組,跳轉過去讀取出域名 資訊。

#include #include #include #include #include #include #include #include #define DNS_SVR “211。68。71。4” #define DNS_HOST 0x01#define DNS_CNAME 0x05 int socketfd;struct sockaddr_in dest; static void send_dns_request(const char *dns_name); static voidparse_dns_response(); /** * Generate DNS question chunk */static void generate_question(const char *dns_name , unsigned char *buf , int *len); /** * Check whether the current byte is * a dns pointer or a length */static intis_pointer(int in); /** * Parse data chunk into dns name * @param chunk The complete response chunk * @param ptr The pointer points to data * @param out This will be filled with dns name * @param len This will be filled with the length of dns name */static voidparse_dns_name(unsigned char *chunk , unsigned char *ptr , char *out , int *len); int main(int argc , char *argv[]){ if(argc != 2){ printf(“Usage : %s \n” , argv[0]); exit(-1); } socketfd = socket(AF_INET , SOCK_DGRAM , 0); if(socketfd < 0){ perror(“create socket failed”); exit(-1); } bzero(&dest , sizeof(dest)); dest。sin_family = AF_INET; dest。sin_port = htons(53); dest。sin_addr。s_addr = inet_addr(DNS_SVR); send_dns_request(argv[1]); parse_dns_response(); return 0;} static void parse_dns_response(){ unsigned char buf[1024]; unsigned char *ptr = buf; struct sockaddr_in addr; char *src_ip; int n , i , flag , querys , answers; int type , ttl , datalen , len; char cname[128] , aname[128] , ip[20] , *cname_ptr; unsigned char netip[4]; size_t addr_len = sizeof(struct sockaddr_in); n = recvfrom(socketfd , buf , sizeof(buf) , 0 , (struct sockaddr*)&addr , &addr_len); ptr += 4; /* move ptr to Questions */ querys = ntohs(*((unsigned short*)ptr)); ptr += 2; /* move ptr to Answer RRs */ answers = ntohs(*((unsigned short*)ptr)); ptr += 6; /* move ptr to Querys */ /* move over Querys */ for(i= 0 ; i < querys ; i ++){ for(;;){ flag = (int)ptr[0]; ptr += (flag + 1); if(flag == 0) break; } ptr += 4; } printf(“————————————————-\n”); /* now ptr points to Answers */ for(i = 0 ; i < answers ; i ++){ bzero(aname , sizeof(aname)); len = 0; parse_dns_name(buf , ptr , aname , &len); ptr += 2; /* move ptr to Type*/ type = htons(*((unsigned short*)ptr)); ptr += 4; /* move ptr to Time to live */ ttl = htonl(*((unsigned int*)ptr)); ptr += 4; /* move ptr to Data lenth */ datalen = ntohs(*((unsigned short*)ptr)); ptr += 2; /* move ptr to Data*/ if(type == DNS_CNAME){ bzero(cname , sizeof(cname)); len = 0; parse_dns_name(buf , ptr , cname , &len); printf(“%s is an alias for %s\n” , aname , cname); ptr += datalen; } if(type == DNS_HOST){ bzero(ip , sizeof(ip)); if(datalen == 4){ memcpy(netip , ptr , datalen); inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr)); printf(“%s has address %s\n” , aname , ip); printf(“\tTime to live: %d minutes , %d seconds\n” , ttl / 60 , ttl % 60); } ptr += datalen; } } ptr += 2;} static voidparse_dns_name(unsigned char *chunk , unsigned char *ptr , char *out , int *len){ int n , alen , flag; char *pos = out + (*len); for(;;){ flag = (int)ptr[0]; if(flag == 0) break; if(is_pointer(flag)){ n = (int)ptr[1]; ptr = chunk + n; parse_dns_name(chunk , ptr , out , len); break; }else{ ptr ++; memcpy(pos , ptr , flag); pos += flag; ptr += flag; *len += flag; if((int)ptr[0] != 0){ memcpy(pos , “。” , 1); pos += 1; (*len) += 1; } } } } static int is_pointer(int in){ return ((in & 0xc0) == 0xc0);} static void send_dns_request(const char *dns_name){ unsigned char request[256]; unsigned char *ptr = request; unsigned char question[128]; int question_len; generate_question(dns_name , question , &question_len); *((unsigned short*)ptr) = htons(0xff00); ptr += 2; *((unsigned short*)ptr) = htons(0x0100); ptr += 2; *((unsigned short*)ptr) = htons(1); ptr += 2; *((unsigned short*)ptr) = 0; ptr += 2; *((unsigned short*)ptr) = 0; ptr += 2; *((unsigned short*)ptr) = 0; ptr += 2; memcpy(ptr , question , question_len); ptr += question_len; sendto(socketfd , request , question_len + 12 , 0 , (struct sockaddr*)&dest , sizeof(struct sockaddr));} static voidgenerate_question(const char *dns_name , unsigned char *buf , int *len){ char *pos; unsigned char *ptr; int n; *len = 0; ptr = buf; pos = (char*)dns_name; for(;;){ n = strlen(pos) - (strstr(pos , “。”) ? strlen(strstr(pos , “。”)) : 0); *ptr ++ = (unsigned char)n; memcpy(ptr , pos , n); *len += n + 1; ptr += n; irstr(pos , “。”)){ *ptr = (unsigned char)0; ptr ++; *len += 1; break; } pos += n + 1; } *((unsigned short*)ptr) = htons(1); *len += 2; ptr += 2; *((unsigned short*)ptr) = htons(1); *len += 2;}

Android網路框架學習——DNS解析與Socket通訊原則

以上是對網路框架中的DNS解析與socket通訊原則的淺析,這些都屬於OKhttp網路框架中的部分內容;如果想深入學習Android的 網路框架;我這裡推薦資料

《OKhttp手冊》文件私信“手冊”獲取↓↓↓

,讓你更方便的深入學習網路框架。

簡述OKhttp優點

OkHttp是一個優秀的網路請求框架,它有以下預設特性:

支援HTTP/2,允許所有同一個主機地址的請求共享同一個socket連線

連線池減少請求延時

透明的GZIP壓縮減少響應資料的大小

快取響應內容,避免一些完全重複的請求

私信“手冊”可領取!