數據包發送少收到多(弱電工程人員必備的常用網絡命令)
數據包發送少收到多文章列表:
弱電工程人員必備的常用網絡命令
前言:
現在的弱電工程可以成為網絡工程了,基本上全是數字化、網絡化了,所以我們需要掌握一定的網絡知識,今天就分享一些常用的網絡命令,設備調試、維護的時候用的到網絡命令。
ping命令
ping是個使用頻率極高的實用程序,主要用于確定網絡的連通性。這對確定網絡是否正確連接,以及網絡連接的狀況十分有用。簡單的說,ping就是一個測試程序,如果ping運行正確,大體上就可以排除網絡訪問層、網卡、Modem的輸入輸出線路、電纜和路由器等存在的故障,從而縮小問題的范圍。
ping能夠以毫秒為單位顯示發送請求到返回應答之間的時間量。如果應答時間短,表示數據報不必通過太多的路由器或網絡,連接速度比較快。ping還能顯示TTL(Time To Live,生存時間)值,通過TTL值可以推算數據包通過了多少個路由器。
1、命令格式
ping 主機名
ping 域名
ping IP地址
如圖所示,使用ping命令檢查到IP地址210.43.16.17的計算機的連通性,該例為連接正常。共發送了四個測試數據包,正確接收到四個數據包。
2、ping命令的基本應用
一般情況下,用戶可以通過使用一系列ping命令來查找問題出在什么地方,或檢驗網絡運行的情況。下面就給出一個典型的檢測次序及對應的可能故障:
① ping 127.0.0.1
如果測試成功,表明網卡、TCP/IP協議的安裝、IP地址、子網掩碼的設置正常。如果測試不成功,就表示TCP/IP的安裝或設置存在有問題。
② ping 本機IP地址
如果測試不成功,則表示本地配置或安裝存在問題,應當對網絡設備和通訊介質進行測試、檢查并排除。
③ ping局域網內其他IP
如果測試成功,表明本地網絡中的網卡和載體運行正確。但如果收到0個回送應答,那么表示子網掩碼不正確或網卡配置錯誤或電纜系統有問題。
④ ping 網關IP
這個命令如果應答正確,表示局域網中的網關路由器正在運行并能夠做出應答。
⑤ ping 遠程IP
如果收到正確應答,表示成功的使用了缺省網關。對于撥號上網用戶則表示能夠成功的訪問Internet(但不排除ISP的DNS會有問題)。
⑥ ping localhost
local host是系統的網絡保留名,它是127.0.0.1的別名,每臺計算機都應該能夠將該名字轉換成該地址。否則,則表示主機文件(/Windows/host)中存在問題。
⑦ ping www.yahoo.com(一個著名網站域名)
對此域名執行Ping命令,計算機必須先將域名轉換成IP地址,通常是通過DNS服務器。如果這里出現故障,則表示本機DNS服務器的IP地址配置不正確,或它所訪問的DNS服務器有故障。
如果上面所列出的所有ping命令都能正常運行,那么計算機進行本地和遠程通信基本上就沒有問題了。但是,這些命令的成功并不表示你所有的網絡配置都沒有問題,例如,某些子網掩碼錯誤就可能無法用這些方法檢測到。
3、ping命令的常用參數選項
ping IP -t:連續對IP地址執行ping命令,直到被用戶以Ctrl C中斷。
ping IP -l 2000:指定ping命令中的特定數據長度(此處為2000字節),而不是缺省的32字節。
ping IP -n 20:執行特定次數(此處是20)的ping命令。
ipconfig命令
ipconfig實用程序可用于顯示當前的TCP/IP配置的設置值。這些信息一般用來檢驗人工配置的TCP/IP設置是否正確。
而且,如果計算機和所在的局域網使用了動態主機配置協議DHCP,使用ipconfig命令可以了解到你的計算機是否成功地租用到了一個IP地址,如果已經租用到,則可以了解它目前得到的是什么地址,包括IP地址、子網掩碼和缺省網關等網絡配置信息。
下面給出最常用的選項:
1、ipconfig
當使用不帶任何參數選項ipconfig命令時,顯示每個已經配置了的接口的IP地址、子網掩碼和缺省網關值。
2、ipconfig /all
當使用all選項時,ipconfig能為DNS和WINS服務器顯示它已配置且所有使用的附加信息,并且能夠顯示內置于本地網卡中的物理地址(MAC)。如果IP地址是從DHCP服務器租用的,ipconfig將顯示DHCP服務器分配的IP地址和租用地址預計失效的日期。圖為運行ipconfig /all命令的結果窗口。
3、ipconfig /release和ipconfig /renew
這兩個附加選項,只能在向DHCP服務器租用IP地址的計算機使用。如果輸入ipconfig /release,那么所有接口的租用IP地址便重新交付給DHCP服務器(歸還IP地址)。如果用戶輸入ipconfig /renew,那么本地計算機便設法與DHCP服務器取得聯系,并租用一個IP地址。大多數情況下網卡將被重新賦予和以前所賦予的相同的IP地址。
ARP命令(地址轉換協議)
ARP是TCP/IP協議族中的一個重要協議,用于確定對應IP地址的網卡物理地址。
使用arp命令,能夠查看本地計算機或另一臺計算機的ARP高速緩存中的當前內容。此外,使用arp命令可以人工方式設置靜態的網卡物理地址/IP地址對,使用這種方式可以為缺省網關和本地服務器等常用主機進行本地靜態配置,這有助于減少網絡上的信息量。
按照缺省設置,ARP高速緩存中的項目是動態的,每當向指定地點發送數據并且此時高速緩存中不存在當前項目時,ARP便會自動添加該項目。
常用命令選項:
① arp –a:用于查看高速緩存中的所有項目。
② arp -a IP:如果有多個網卡,那么使用arp -a加上接口的IP地址,就可以只顯示與該接口相關的ARP緩存項目。
③ arp -s IP 物理地址:向ARP高速緩存中人工輸入一個靜態項目。該項目在計算機引導過程中將保持有效狀態,或者在出現錯誤時,人工配置的物理地址將自動更新該項目。
④ arp -d IP:使用本命令能夠人工刪除一個靜態項目。
traceroute命令
掌握使用traceroute命令測量路由情況的技能,即用來顯示數據包到達目的主機所經過的路徑。
traceroute命令的基本用法是,在命令提示符后鍵入“tracert host_name”或“tracert ip_address”,其中,tracert是traceroute在Windows操作系統上的稱呼。
輸出有5列:
第一列是描述路徑的第n跳的數值,即沿著該路徑的路由器序號;
第二列是第一次往返時延;
第三列是第二次往返時延;
第四列是第三次往返時延;
第五列是路由器的名字及其輸入端口的IP地址。
如果源從任何給定的路由器接收到的報文少于3條(由于網絡中的分組丟失),traceroute在該路由器號碼后面放一個星號,并報告到達那臺路由器的少于3次的往返時間。
此外,tracert命令還可以用來查看網絡在連接站點時經過的步驟或采取哪種路線,如果是網絡出現故障,就可以通過這條命令查看出現問題的位置。
思 考:
【測試大型網絡的路由】:
(1)多嘗試幾次“ping www.sina.com.cn”操作,比較得到的新浪網的IP地址。如果兩次ping得到的IP地址不同,試考慮其中的原因(如考慮到負載均衡)。然后,針對這些不同的IP地址,執行“tracert ip_address”命令,觀察分析輸出的結果是否有差異。
(2)對于大型網絡中的某站點進行traceroute測試,記錄測試結果。觀察其中是否出現第n跳的時延小于第n-1跳的時延情況。試分析其中原因(提示:可分別考慮時延的各個構成成分在總時延中所起的作用)。
(3)在一天的不同時段內,用traceroute程序多次測試從固定主機到遠程固定IP地址的主機的路由。試分析比較測量數據,觀察該路由是否有變化?如果有變化,該變化頻繁嗎?
route命令
大多數主機一般都是駐留在只連接一臺路由器的網段上。由于只有一臺路由器,因此不存在選擇使用哪一臺路由器將數據包發送到遠程計算機上去的問題,該路由器的IP地址可作為該網段上所有計算機的缺省網關。
但是,當網絡上擁有兩個或多個路由器時,用戶就不一定想只依賴缺省網關了。實際上可能想讓某些遠程IP地址通過某個特定的路由器來傳遞,而其他的遠程IP則通過另一個路由器來傳遞。在這種情況下,用戶需要相應的路由信息,這些信息儲存在路由表中,每個主機和每個路由器都配有自己獨一無二的路由表。大多數路由器使用專門的路由協議來交換和動態更新路由器之間的路由表。但在有些情況下,必須人工將項目添加到路由器和主機上的路由表中。route命令就是用來顯示、人工添加和修改路由表項目的。該命令可使用如下選項:
1、route print
本命令用于顯示路由表中的當前項目,在單個路由器網段上的輸出結果如圖所示。
2、route add
使用本命令,可以將路由項目添加給路由表。
例如,如果要設定一個到目的網絡209.99.32.33的路由,其間要經過5個路由器網段,首先要經過本地網絡上的一個路由器IP為202.96.123.5,子網掩碼為255.255.255.224,那么用戶應該輸入以下命令:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.5 metric 5
3、route change
可以使用本命令來修改數據的傳輸路由,不過,用戶不能使用本命令來改變數據的目的地。下面這個例子將上例路由改變采用一條包含3個網段的路徑:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.250 metric 3
4、route delete
使用本命令可以從路由表中刪除路由。例如:route delete 209.99.32.33
nslookup命令
命令nslookup的功能是查詢任何一臺機器的IP地址和其對應的域名。它通常需要一臺域名服務器來提供域名。如果用戶已經設置好域名服務器,就可以用這個命令查看不同主機的IP地址對應的域名。
(1)在本地機上使用nslookup命令查看本機的IP及域名服務器地址。
直接鍵入命令,系統返回本機的服務器名稱(帶域名的全稱)和IP地址,并進入以“>”為提示符的操作命令行狀態;鍵入“?”可查詢詳細命令參數;若要退出,需鍵入exit。
(2)查看www.haut.edu.cn的IP。在提示符后輸入要查詢的IP地址或域名并回車即可。
nbtstat命令
使用nbtstat命令可以查看計算機上網絡配置的一些信息。使用這條命令還可以查找出別人計算機上一些私人信息。如果想查看自己計算機上的網絡信息,可以運行nbtstat -n,可以得到你所在的工作組,計算機名以及網卡地址等等;想查看網絡上其他的電腦情況,就運行nbtstat -a *.*.*.*,此處的*.*.*.*用IP地址代替就會返回得到那臺主機上的一些信息。
netstat命令
學習使用netstat命令,以了解網絡當前的狀態。
netstat命令能夠顯示活動的TCP連接、計算機偵聽的端口、以太網統計信息、IP路由表、IPv4統計信息(對于IP、ICMP、TCP和UDP協議)以及IPv6統計信息(對于IPv6、ICMPv6、通過IPv6的TCP以及UDP協議)。使用時如果不帶參數,netstat顯示活動的TCP連接。
下面給出netstat的一些常用選項:
① netstat –a:-a選項顯示所有的有效連接信息列表,包括已建立的連接(ESTABLISHED),也包括監聽連接請求(LISTENING)的那些連接。
② netstat –n:以點分十進制的形式列出IP地址,而不是象征性的主機名和網絡名。
③ netstat -e:-e選項用于顯示關于以太網的統計數據。它列出的項目包括傳送的數據包的總字節數、錯誤數、刪除數、數據包的數量和廣播的數量。這些統計數據既有發送的數據包數量,也有接收的數據包數量。使用這個選項可以統計一些基本的網絡流量。
④ netstat -r:-r選項可以顯示關于路由表的信息,類似于route print命令時看到的信息。除了顯示有效路由外,還顯示當前有效的連接。
上圖顯示的是一個路由表,其中:Network Destination表示目的網絡,0.0.0.0表示不明網絡,這是設置默認網關后系統自動產生的;127.0.0.0表示本機網絡地址,用于測試;224.0.0.0表示組播地址;255.255.255.255表示限制廣播地址;Netmask表示網絡掩碼,Gateway表示網關,Interface表示接口地址,Metric表示路由跳數。
⑤ netstat -s:-s選項能夠按照各個協議分別顯示其統計數據。這樣就可以看到當前計算機在網絡上存在哪些連接,以及數據包發送和接收的詳細情況等等。如果應用程序(如Web瀏覽器)運行速度比較慢,或者不能顯示Web頁之類的數據,那么可以用本選項來查看一下所顯示的信息。仔細查看統計數據的各行,找到出錯的關鍵字,進而確定問題所在。
net命令
了解Net服務的功能,學會使用Net服務命令解決有關網絡問題。
在命令行鍵入net help command,可以在命令行獲得net命令的語法幫助。例如,要得到關于net accounts命令的幫助信息,可鍵入“net help accounts”。
所有net命令都可以使用/y和/n命令行選項。例如,net stop server命令用于提示用戶確認停止所有依賴的服務器服務,net stop server/y表示確認停止并關閉服務器服務。
下表列出了基本的NET命令及它們的作用:
NET命令的執行結果有許多與其它Windows Server 2003管理工具所得到的結果相似。但是,NET命令可以在一個地方提供所有信息,并可以把結果重定向到打印機或一個標準的文本文件中。
許多服務所使用的網絡命令都以net開頭,這些net命令有一些公用屬性。要看到所有可用的net命令的列表,可以在命令提示符窗口鍵入net/?得到。
手把手教你IP數據包接收過程超詳細
在《深度剖析Linux 網絡中斷下半部處理(看完秒懂)》一文中介紹過,當網卡接收到網絡數據包后,會由網卡驅動通過調用 netif_rx 函數把數據包添加到待處理隊列中,然后喚起網絡中斷下半部處理。
而網絡中斷下半部處理由 net_rx_action 函數完成的,其主要功能就是從待處理隊列中獲取一個數據包,然后根據數據包的網絡層協議類型來找到相應的處理接口來處理數據包。我們通過 圖1 來展示 net_rx_action 函數的處理過程:
圖1 net_rx_action 處理過程
更多linux內核視頻教程文檔資料免費領取后臺私信【內核】自行獲取.
網絡層的處理接口保存在 ptype_base 數組中,其元素的類型為 packet_type 結構,而索引為網絡層協議的類型,所以通過網絡層協議的類型就能找到對應的處理接口,我們先來看看 packet_type 結構的定義:
struct packet_type{ unsigned short type; // 網絡層協議類型 struct net_device *dev; // 綁定的設備對象, 一般設置為NULL int (*func)(struct sk_buff *, struct net_device *, struct packet_type *); // 處理接口 void *data; // 私有數據域, 一般設置為NULL struct packet_type *next;};
ptype_base 數組的定義如下:
static struct packet_type *ptype_base[16];
從 ptype_base 數組的定義可知,其只有 16 個元素,那么如果內核超過 16 種網絡層協議怎么辦?我們可以從網絡層處理接口的注冊函數 dev_add_pack 中找到答案:
void dev_add_pack(struct packet_type *pt){ int hash; br_write_lock_bh(BR_NETPROTO_LOCK); // 如果類型為ETH_P_ALL, 表示需要處理所有協議的數據包 if (pt->type == htons(ETH_P_ALL)) { netdev_nit ; pt->next = ptype_all; ptype_all = pt; } else { // 對網絡層協議類型與15進行與操作, 得到一個 0 到 15 的整數 hash = ntohs(pt->type) & 15; // 通過 next 指針把沖突的處理接口連接起來 pt->next = ptype_base[hash]; ptype_base[hash] = pt; } br_write_unlock_bh(BR_NETPROTO_LOCK);}
從 dev_add_pack 函數的實現可知,內核把 ptype_base 數組當成了哈希表,而鍵值就是網絡層協議類型,哈希函數就是對協議類型與 15 進行與操作。如果有沖突,就通過 next 指針把沖突的處理接口連接起來。
我們再來看看 IP 協議是怎樣注冊處理接口的,如下代碼:
/* /net/ipv4/ip_output.c */static struct packet_type ip_packet_type = { __constant_htons(ETH_P_IP), NULL, ip_rcv, // 處理 IP 協議數據包的接口 (void*)1, NULL, }; void __init ip_init(void){ // 注冊 IP 協議處理接口 dev_add_pack(&ip_packet_type); ... }
從上面的代碼可以看到,在 ip_init 函數中調用了 dev_add_pack(&ip_packet_type) 函數來注冊了 IP 協議的處理接口,而 IP 協議的處理接口為 ip_rcv 函數。
我們再看看 net_rx_action 函數的處理:
static void net_rx_action(struct softirq_action *h){ ... for (;;) { ... { struct packet_type *ptype, *pt_prev; unsigned short type = skb->protocol; // 網絡層協議類型 pt_prev = NULL; ... // 從 ptype_base 數組中查找網絡層處理接口 for (ptype = ptype_base[ntohs(type) & 15]; ptype; ptype = ptype->next) { if (ptype->type == type // 如果協議類型匹配 && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) { atomic_inc(&skb->users); // 如處理IP協議數據包的 ip_rcv() 函數 pt_prev->func(skb, skb->dev, pt_prev); } pt_prev = ptype; } } ... } ... } ... return;}
現在就非常清晰了,就是根據數據包的網絡層協議類型,然后從 ptype_base 數組中找到對應的處理接口處理數據包,如 IP 協議的數據包就調用 ip_rcv 函數處理。
處理IP數據包
通過上面的分析,我們知道當內核接收到一個 IP 數據包后,會調用 ip_rcv 函數處理這個數據包,下面我們來分析一下 ip_rcv 函數的實現:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt){ struct iphdr *iph = skb->nh.iph; // 如果數據包不是發送給本機的, 丟棄數據包 if (skb->pkt_type == PACKET_OTHERHOST) goto drop; // 如果其他地方也在使用數據包, 復制一份新的數據包 if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) goto out; // 數據包的長度比IP頭部的長度還小,不合法丟棄 if (skb->len < sizeof(struct iphdr) || skb->len < (iph->ihl<<2)) goto inhdr_error; // 判斷IP頭部是否合法 if (iph->ihl < 5 // IP頭部長度是否合法 || iph->version != 4 // IP協議版本是否合法 || ip_fast_csum((u8 *)iph, iph->ihl) != 0) // IP校驗和是否正確 goto inhdr_error; { __u32 len = ntohs(iph->tot_len); // 如果數據包的長度比IP頭部的總長度小, 說明數據包不合法, 需要丟棄 if (skb->len < len || len < (iph->ihl<<2)) goto inhdr_error; __skb_trim(skb, len); } // 如果所有驗證都通過, 那么調用 ip_rcv_finish 函數繼續處理數據包 return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); // 這里是丟棄數據包的處理inhdr_error: IP_INC_STATS_BH(IpInHdrErrors);drop: kfree_skb(skb);out: return NET_RX_DROP;}
ip_rcv 函數主要對數據包的合法性進行驗證,如果數據包是合法的,那么就調用 ip_rcv_finish 函數繼續對數據包進行處理。
我們繼續分析 ip_rcv_finish 函數的實現:
static inline int ip_rcv_finish(struct sk_buff *skb){ struct net_device *dev = skb->dev; struct iphdr *iph = skb->nh.iph; if (skb->dst == NULL) { // 如果數據包的輸入路由緩存還沒設置 // 根據目標IP地址獲取數據包的輸入路由緩存 if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) goto drop; } ... // 如果數據包是發送給本機的,那么就調用 ip_local_deliver 進行處理 return skb->dst->input(skb); drop: kfree_skb(skb); return NET_RX_DROP;}
為了簡單起見,我們去掉了對 IP 選項的處理。在上面的代碼中,如果數據包的輸入路由緩存還沒設置,那么先調用 ip_route_input 函數獲取數據包的輸入路由緩存(ip_route_input 函數將會在 路由子系統 一章介紹,暫時可以忽略這個函數)。
設置好數據包的路由緩存后,就調用路由緩存的 input 方法處理數據包。如果數據包是發送給本機的,那么路由緩存的 input 方法將會被設置為 ip_local_deliver(由 ip_route_input 函數設置)。
所有,如果數據包是發送給本機,那么最終會調用 ip_local_deliver 函數處理數據包,我們繼續來分析 ip_local_deliver 函數:
int ip_local_deliver(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; // 如果當前數據包是一個IP分片, 那么先對數據包進行分片處理 if (iph->frag_off & htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb); if (!skb) return 0; } // 接著調用 ip_local_deliver_finish 函數處理數據包 return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish);}
ip_local_deliver 函數首先判斷數據包是否為一個 IP 分片(IP 分片將在下一篇文章介紹,暫時可以忽略),如果是就調用 ip_defrag 函數對數據包進行分片重組處理。如果數據包不是一個分片或者分片重組成功,那么最終調用 ip_local_deliver_finish 函數處理數據包。
ip_local_deliver_finish 函數是 IP 層處理數據包的最后一步,我們接著分析 ip_local_deliver_finish 函數的實現:
static inline int ip_local_deliver_finish(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; // 獲取數據包的IP頭部 ... skb->h.raw = skb->nh.raw iph->ihl * 4; // 設置傳輸層協議頭部指針 { int hash = iph->protocol & (MAX_INET_PROTOS - 1); // 從IP頭部獲取傳輸層協議類型 struct sock *raw_sk = raw_v4_htable[hash]; struct inet_protocol *ipprot; int flag; ... ipprot = (struct inet_protocol *)inet_protos[hash]; // 傳輸層協議處理函數 flag = 0; if (ipprot != NULL) { // 調用傳輸層協議處理函數處理數據包 if (raw_sk == NULL && ipprot->next == NULL && ipprot->protocol == iph->protocol) { return ipprot->handler(skb, (ntohs(iph->tot_len)-(iph->ihl*4))); } else { flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL)); } } ... } return 0;}
在上面代碼中,我們省略對原始套接字的處理(原始套接字將會在 原始套接字 一章中介紹)。ip_local_deliver_finish 函數的主要工作如下:
通過數據包的 IP 頭部獲取到上層協議(傳輸層)類型。
根據傳輸層協議類型從 inet_protos 數組中查找對應的處理函數。
調用傳輸層協議的處理函數處理數據包。
inet_protos 數組保存了傳輸層協議的處理函數,其的定義如下:
struct inet_protocol{ int (*handler)(struct sk_buff *skb, unsigned short len); // 協議的處理函數 unsigned char protocol; // 協議類型 struct inet_protocol *next; // 解決沖突 ...};#define MAX_INET_PROTOS32struct inet_protocol *inet_protos[MAX_INET_PROTOS];
不同的傳輸層協議處理函數,會根據其協議類型的值保存到 inet_protos 數組中。由于 inet_protos 數組只有32個元素,所以保存處理函數時,需要將協議值與32進行取模操作,得到一個 0 ~ 31 的值,然后把處理函數保存到 inet_protos 數組對應位置上。如果有多個協議發生沖突,那么就通過 next 字段連接起來。
通過調用 inet_add_protocol 函數,可以向 inet_protos 數組注冊傳輸層協議的處理函數。例如 TCP協議 的處理函數定義如下:
static struct inet_protocol TCP_protocol = { tcp_v4_rcv, /* TCP handler */ IPPROTO_TCP, /* protocol ID */ ...};
所以,當接收到一個 TCP 協議數據包時,將會調用 tcp_v4_rcv 函數處理此數據包。
最后,我以一幅圖來展示處理 IP 數據包的函數調用鏈:
一分鐘實現內網穿透ngrok服務器搭建
一分鐘實現內網穿透(ngrok服務器搭建)
簡單來說內網穿透的目的是:讓外網能訪問你本地的應用,例如在外網打開你本地http://127.0.0.1指向的Web站點。
最近公司的花生殼到期了,要續費,發現價格一直在漲,都是5年以上的老用戶,旗艦版都沒有實現內網完全穿透,打算自己動手替換這個服務,中間走了不少的彎路,這里記錄一些文字為大家提供參考。
隨著開發與運行移動互聯網的應用越來越多對打通內外網的需要也更加迫切,如微信開發、IOS與Android開發等。
雖然租用VPS、ECS等服務器可以解決很多問題但高性能的外網服務器價格非常貴還有數據安全問題,我選擇的是公網服務器僅做代理與輕量應用,復雜的應用部署到內網服務器再穿透訪問。
一、內網穿透概要
為了理解內網穿透我們先來了解幾個概念:
1.1、IP地址
網絡中唯一定位一臺設備的邏輯地址,類似我們的電話號碼
在互聯網中我們訪問一個網站或使用一個網絡服務最終都需要通過IP定位到每一臺主機,如訪問baidu網站:
其中119.75.213.61就是一個公網的IP地址,他最終指向了一臺服務器。
IP地址是IP協議提供的一種統一的地址格式,它為互聯網上的每一個網絡和每一臺主機分配一個邏輯地址,以此來屏蔽物理地址的差異。
內網IP可以同時出現在多個不同的局域網絡中,如A公司的U1用戶獲得了192.168.0.5,B公司的U3用戶也可以獲得192.168.0.5;但公網IP是唯一的,因為我們只有一個Internet。
//局域網可使用的網段(私網地址段)有三大段:10.0.0.0~10.255.255.255(A類)172.16.0.0~172.31.255.255(B類)192.168.0.0~192.168.255.255(C類)
1.2、域名
域名是IP的別名,便于記憶,域名最終通過DNS解析成IP地址。
IP V4是一個32位的數字,IP V6有128位,要記住一串毫無意義的數字非常困難,域名解決了這個問題。
如www.zhangguo.com.cn就是一個域名,cn表示地區,com表示商業機構,zhangguo是公司名稱,www是主機名
DNS查詢過程如下,最終將域名變成IP地址
1.3、NAT
NAT(Network Address Translation)即網絡地址轉換,NAT能將其本地地址轉換成全球IP地址。
內網的一些主機本來已經分配到了本地IP地址(如局域網DHCP分配的IP),但現在又想和因特網上的主機通信(并不需要加密)時,可使用NAT方法。
通過使用少量的公有IP 地址代表較多的私有IP 地址的方式,將有助于減緩可用的IP地址空間的枯竭。
NAT不僅能解決了lP地址不足與共享上網的問題,而且還能夠有效地避免來自網絡外部的攻擊,隱藏并保護網絡內部的計算機。
多路由器可完成NAT功能。
NAT的實現方式:
靜態轉換是指將內部網絡的私有IP地址轉換為公有IP地址,IP地址對是一對一。
動態轉換是指將內部網絡的私有IP地址轉換為公用IP地址時,IP地址是不確定的,是隨機的。
端口多路復用(Port address Translation,PAT),內部網絡的所有主機均可共享一個合法外部IP地址實現對Internet的訪問,從而可以最大限度地節約IP地址資源。同時又可隱藏網絡內部的所有主機,有效避免來自internet的攻擊。因此,目前網絡中應用最多的就是端口多路復用方式。
應用程序級網關技術(Application Level Gateway)ALG:傳統的NAT技術只對IP層和傳輸層頭部進行轉換處理,ALG它能對這些應用程序在通信時所包含的地址信息也進行相應的NAT轉換。
1.4、Proxy
Proxy即代理,被廣泛應用于計算機領域,主要分為正向代理與反向代理:
1.4.1、正向代理
比如X花店代A,B,C,D,E五位男生向Candy女生送匿名的生日鮮花,這里的X花店就是5位顧客的代理,花店代理的是客戶,隱藏的是客戶。這就是我們常說的代理。
正向代理隱藏了真實的請求客戶端。服務端不知道真實的客戶端是誰,客戶端請求的服務都被代理服務器代替來請求,某些科學上網工具扮演的就是典型的正向代理角色。用瀏覽器訪問http://www.google.com時被墻了,于是你可以在國外搭建一臺代理服務器,讓代理幫我去請求google.com,代理把請求返回的相應結構再返回給我。
當多個客戶端訪問服務器時服務器不知道真正訪問自己的客戶端是那一臺。正向代理中,proxy和client同屬一個LAN,對server透明;
1.4.2、反向代理
撥打10086客服電話,接線員可能有很多個,調度器會智能的分配一個接線員與你通話。這里的調度器就是一個代理,只不過他代理的是接線員,客戶端不能確定真正與自己通話的人,隱藏與保護的是目標對象。
反向代理隱藏了真實的服務端,當我們請求 ww.baidu.com 的時候,就像撥打10086一樣,背后可能有成千上萬臺服務器為我們服務,但具體是哪一臺,你不知道,也不需要知道,你只需要知道反向代理服務器是誰就好了,ww.baidu.com 就是我們的反向代理服務器,反向代理服務器會幫我們把請求轉發到真實的服務器那里去。Nginx就是性能非常好的反向代理服務器,用來做負載均衡。
反向代理中,proxy和server同屬一個LAN,對client透明。
了解更多關于代理內容請點擊這里。
1.5、DDNS
DDNS即動態域名解析,是將用戶的動態IP地址映射到一個固定的域名解析服務上,用戶每次連接網絡的時候,客戶端程序就會通過信息傳遞把該主機的動態IP地址傳送給位于服務商主機上的服務器程序,服務程序負責提供DNS服務并實現動態域名解析。就是說DDNS捕獲用戶每次變化的IP地址,然后將其與域名相對應,這樣域名就可以始終解析到非固定IP的服務器上,互聯網用戶通過本地的域名服務器獲得網站域名的IP地址,從而可以訪問網站的服務。
1.6、為什么需要內網穿透
當內網中的主機沒有靜態IP地址要被外網穩定訪問時可以使用內網穿透
在互聯網中唯一定位一臺主機的方法是通過公網的IP地址,但固定IP是一種非常稀缺的資源,不可能給每個公司都分配一個,且許多中小公司不愿意為高昂的費用買單,多數公司直接或間接的撥號上網,電信部門會給接入網絡的用戶分配IP地址,以前上網用戶少的時候基本分配的都是臨時的靜態IP地址,租約過了之后可能會更換成另一個IP地址,這樣外網訪問就不穩定,因為內網的靜態IP地址一直變化,為了解決這個問題可以使用動態域名解析的辦法變換域名指向的靜態IP地址。但是現在越來越多的上網用戶使得臨時分配的靜態IP地址也不夠用了,電信部門開始分配一些虛擬的靜態IP地址,這些IP是公網不能直接訪問的,如以125開頭的一些IP地址,以前單純的動態域名解析就不好用了。
1.7、內網穿透的定義與障礙
簡單來說實現不同局域網內的主機之間通過互聯網進行通信的技術叫內網穿透。
障礙一:位于局域網內的主機有兩套 IP 地址,一套是局域網內的 IP 地址,通常是動態分配的,僅供局域網內的主機間通信使用;一套是經過網關轉換后的外網 IP 地址,用于與外網程序進行通信。
障礙二:位于不同局域網內的兩臺主機,即使是知道了對方的 IP 地址和端口號,“一廂情愿”地將數據包發送過去,對方也是接收不到的。
因為出于安全起見,除非是主機主動向對方發出了連接請求(這時會在該主機的數據結構中留下一條記錄),否則,當主機接收到數據包時,如果在其數據結構中查詢不到對應的記錄,那些不請自來的數據包將會被丟棄。
解決辦法:要想解決以上兩大障礙,我們需要借助一臺具有公網 IP 的服務器進行橋接。
二、常見的內網穿透產品
2.1、花生殼
花生殼既是內網穿透軟件、內網映射軟件,也是端口映射軟件。規模最大,較正規,完善。
收費高,使用簡單
官網:http://www.oray.com/
2.2、Nat123
nat123是內網端口映射與動態域名解析軟件,在內網啟動映射后,可在外網訪問連接內網網站等應用。整個網站我都沒有找到客服電話,網友發了一些反面的評價
收費,使用簡單
官網:http://www.nat123.com
2.3、NATAPP
NATAPP基于ngrok的國內內網穿透服務,免費版會強制更換域名,臨時用一下可以
收費,使用簡單
官網:https://natapp.cn/
2.4、frp與其它
frp 是一個高性能的反向代理應用,可以幫助您輕松地進行內網穿透,對外網提供服務,支持 tcp, http, https 等協議類型,并且 web 服務支持根據域名進行路由轉發。
開源免費
使用相對復雜,需要代理服務器支持
官網:https://github.com/fatedier/frp
文檔:查看幫助文檔,簡書示例
利用處于內網或防火墻后的機器,對外網環境提供 http 或 https 服務。
對于 http, https 服務支持基于域名的虛擬主機,支持自定義域名綁定,使多個域名可以共用一個80端口。
利用處于內網或防火墻后的機器,對外網環境提供 tcp 和 udp 服務,例如在家里通過 ssh 訪問處于公司內網環境內的主機。
因為frp 仍然處于前期開發階段,未經充分測試與驗證,不推薦用于生產環境,所有我選擇了ngrok,資料比較多。
還有如圣劍內網通、ngrok(開源免費)、更多辦法
三、ngrok
ngrok是一個反向代理,通過在公共的端點和本地運行的Web服務器之間建立一個安全的通道。ngrok可捕獲和分析所有通道上的流量,便于后期分析與響應。
開源免費
官網:https://ngrok.com/
源碼:https://github.com/inconshreveable/ngrok
ngrok1.x開源,ngrok2.x不開源
ngrok使用go語言開發,源代碼分為客戶端與服務器端。
國內免費服務器:http://ngrok.ciqiuwl.cn/,更多免費服務器請大家挖掘,資源共享,我隨時更新:)
如果有服務器,僅客戶端的使用是不復雜的,以上面的免費服務器為示例完成內網穿透
現在假定我的本地已成功部署了一個網站,訪問地址為127.0.0.1,想內網穿透后被公網上的用戶訪問,一般步驟如下:
步驟1、下載windows版本的客戶端,解壓。一般在為你提供代理服務器的網站上找你要下載的客戶端:
步驟2、在命令(cmd)行下進入到ngrok客戶端目錄下
步驟3、執行 ngrok -config=ngrok.cfg -subdomain xxx 80 //(xxx 是你自定義的域名前綴),建議批處理
如果連接成功,會提示如下信息:
這一步如果你認為太麻煩,可以直接運行目錄下的start.bat批處理文件就不用進DOS環境了。運行start.bat直接跳過2,3步
步驟4、如果開啟成功 你就可以使用 xxx.ngrok.xiaomiqiu.cn 來訪問你本機的 127.0.0.1:80 的服務了,當然你必須確定的是你本機的Web是可以正常訪問的。
注意:
如果你自己有頂級域名,想通過自己的域名來訪問本機的項目,那么先將自己的頂級域名解析到120.25.161.137(域名需要已備案哦,80端口必須備案),然后執行 ngrok -config=ngrok.cfg -hostname xxx.xxx.xxx 80 //(xxx.xxx.xxx是你自定義的頂級域名)
四、ubuntu下生成ngrok服務器主程序
4.1、步驟與先決條件
如果你只是臨時穿透或調試用,到第三步基本就可以了,但如果想作為穩定的商業服務,用別人的服務器還是受制于人,這里我們準備搭建自己的ngrok服務器。大致的步驟如下:
ngrok服務器可以是多種平臺,如windows、linux(CentOS、Debian、Ubuntu等)、Mac OS等。
編譯源代碼生成應用強烈建議大家使用linux環境,windows肯定可以成功,但非常麻煩,我在windows操作系統上兜了一個大圈圈。
先決條件:
a)、您有一臺公網上的服務器,如阿里云的ECS
b)、您有一個域名,最好ICP備案成功,不然80端口沒有辦法使用,不過像微信開發是不使用80端口的,可以用nginx代理轉換。
4.2、安裝ubuntu操作系統
在linux環境下編譯ngrok的源代碼比windows下 方便很多,這里我們選擇使用ubuntu,獲得ubuntu的方法有如下幾種:
1)、全新安裝ubuntu系統
2)、申請VPS服務器, 阿里云、騰訊云、華為云、百度云、新浪云等,僅編譯一下這種方法不錯
3)、在虛擬機中安裝ubuntu系統
綜合考慮我選擇了在虛擬機中安裝ubuntu操作系統
4.2.1、安裝VMware虛擬機
VMware Workstation是一款功能強大的虛擬機軟件,在不影響本機操作系統的情況下,用戶可以在虛擬機中同時運行不同版本的操作系統,用于開發、測試以及部署工作。
VMware Workstation 12 pro下載:VMware-workstation-full-12.1.0-3272444.exe
序列號:5A02H-AU243-TZJ49-GTC7K-3C61N(商業應用請購買正式版權,這里僅為學習使用)
1)、雙擊VMware Workstation 12安裝文件,或者右鍵管理員身份打開,提示是否允許更改,點擊是;
2)、打開VMware安裝向導,點擊下一步;
3)、VMware Workstation 12激活步驟:
方法一、首次開啟直接輸入上文密鑰,即可激活;
方法二、首次開啟選擇試用,進入試用后按一下步驟激活:
a、打開虛擬機主界面,點擊“幫助”—“輸入許可證密鑰”;
b、在密鑰輸入框輸入永久許可證密鑰5A02H-AU243-TZJ49-GTC7K-3C61N,確定;更多
4.2.2、安裝ubuntu到虛擬機
1)、下載ubuntu操作系統鏡像
下載地址:https://www.ubuntu.com/download/desktop
這里我下載的是ubuntu-16.04.3-desktop-amd64.iso
2)、在VMware中安裝ubuntu
打開VMware點擊“創建新的虛擬機”
向導選擇自定義
然后下一步再下一步,直到這里,稍后再安裝系統
后面設置處理器和內存的,電腦配置好的可以試試,否則采用默認的,博主這里是采用默認的,然后下一,直到這里,選擇將虛擬機存儲為單個磁盤:
個人建議至少20G硬盤空間,內存建議給1.5G,當然也要看電腦本身的配置,1G的內存跑起來比較卡。
其它的步驟比較簡單,更多細節可以參考這里,《VMware Ubuntu安裝詳細過程》。
4.2.3、配置ubuntu系統
當ubuntu系統安裝成功后,在虛擬機中可以啟動ubuntu系統,啟動后的系統如下:
ubuntu系統的使用還是有許多內容的,這里需要設置的內容如下:
a)、設置上網
就是在ubuntu中可以訪問外網,可以使用多種形式
b)、設置語言
可以選擇使用中文版的ubuntu語言環境
c)、設置屏幕分辨率
如果不設置默認的屏幕比較小
d)、設置以root超級管理員的身份登錄
許多操作要求管理身份
e)、安裝VMware Tools工具
只有在VMware虛擬機中安裝好了VMware Tools,才能實現主機與虛擬機之間的文件共享,同時可支持自由拖拽的功能,鼠標也可在虛擬機與主機之間自由移動(不用再按ctrl alt),且虛擬機屏幕也可實現全屏化。
VMware Tools是VMware虛擬機中自帶的一種增強工具,相當于VirtualBox中的增強功能(Sun VirtualBox Guest Additions),是VMware提供的增強虛擬顯卡和硬盤性能、以及同步虛擬機與主機時鐘的驅動程序。
注意如果這里是灰色的需要您將linux.iso鏡像加載到虛擬光驅中,一般在VM的安裝目錄下有,如果沒有您需要自行下載。
說明:ubuntu的使用不是本文的重點,相關操作請大家自行查找。
4.3、生成ngrok服務器與客戶端應用程序
4.3.1. 導出源代碼
ngrok的源代碼托管在github上,可以先在ubuntu下安裝git再將ngrok的源代碼克隆到本地。
其實也可以直接下載到本地后解壓,這里使用命令行完成。
啟動ubuntu,開打命令行(終端),如下所示:
以root身份執行如下命令:
mkdir ngrok #創建名稱為ngrok的目錄 apt-get update #更新包管理器 apt-get install git #安裝git git clone https://github.com/inconshreveable/ngrok.git ngrok2 #將ngrok源代碼克隆回本地
成功執行后如下所示:
導出成功后的源代碼:
PS. 直接在服務器上下載的話實在太慢,可以先在本地下載好,然后用ftp放到服務器上去直接用,如果安裝了VMware tools直接拖進去就可以了。
4.3.2. 安裝Go語言開發環境
直接在命令模式下執行如下指令:
apt-get install golang #安裝go語言
執行結果如下:
4.3.3. 更改ngrok域名
在自己的域名管理中添加解析A記錄,如下所示:
將*.ngrok與ngrok都指向您的主機IP。
默認的域名是ngrok自己的,要替換成您自己的域名
export GOPATH=/usr/local/ngrok/ #設置環境變量,Go語言的安裝位置export NGROK_DOMAIN="ngrok.yourdomain.com" #設置環境變量,ngrok域名
PS. ngrok名稱可以任意,推薦名稱為ngrok或者tunnel
4.3.4. 為域名生成證書
openssl genrsa -out rootCA.key 2048openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pemopenssl genrsa -out server.key 2048openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csropenssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 5000
生成后的結果如下:
證書如下:
4.3.5. 拷貝證書到指定位置
cp rootCA.pem assets/client/tls/ngrokroot.crt #復制rootCA.pem到assets/client/tls/并更名為ngrokroot.crtcp server.crt assets/server/tls/snakeoil.crt #復制server.crt到assets/server/tls/并更名為snakeoil.crtcp server.key assets/server/tls/snakeoil.key #復制server.key到assets/server/tls/并更名為snakeoil.key
運行結果:
4.3.6. 編譯
由于go語言的特性,在編譯時直接生成機器碼,所以在運行過程中并不需要go的環境(非托管應用)。在ngrok目錄下,運行一下命令分別生成對應的客戶端與服務端。
#win服務端GOOS=windows GOARCH=386 make release-server #win客戶端GOOS=windows GOARCH=386 make release-client#linux服務端GOOS=linux GOARCH=386 make release-server#linux客戶端GOOS=linux GOARCH=386 make release-client
生成完成后,在工作目錄的bin文件夾下,產生對應的文件。以編譯windows平臺為例,會產生“ngrok.exe”與“ngrokd.exe”這兩個文件,前者客戶端,后者需要運行在公網服務器上。
因為項目中引用了一些外部資源,生成會耗費一些時間,對網絡也有一定的要求,太慢會中短,命令執行下如:
生成結果:
這里我還生成了兩個運行在windows服務器與客戶端的應用:
ngrok.exe是客戶端,ngrokd.exe是服務端,下面是比較連續的操作結果。
五、部署服務器端主程序
5.1、部署到Windows Server服務器
將生成的ngrokd.exe文件復制到windows服務器中,當然如果要部署到linux中也是沒有問題的。
這里我將ngrokd.exe放在c:grokeServer目錄下:
為了方便,我編寫了一個批處理文件:ngrokserver2.bat
ngrokd.exe -tlsKey="snakeoil.key" -tlsCrt="snakeoil.crt" -domain="ngrok.你的域名.com" -httpAddr=":801" -httpsAddr=":802"
點擊批處理運行結果如下:
綁定的域名換成自己的域名,http使用801端口,https使用802端口,供客戶端連接的管道端口設置為4443端口,必須前面的域名相同。
為了安全許多服務器會將端口屏蔽,我使用的是ECS服務器,默認801,802都是關閉的,需要手動開啟,在阿里云的后臺添加開放的端口就可了:
5.2、一鍵部署ngrok服務器(CentOS、Debian、Ubuntu)
如果編譯生成ngrok的源代碼生成應用太麻煩,你可以選擇網友寫的工具,支持一鍵部署到安裝平臺:CentOS、Debian、Ubuntu。
https://github.com/clangcn/ngrok-one-key-install
六、部署ngrok客戶端
這里的客戶端就是您的web應用程序所運行的主機,將ubuntu生成的ngrok.exe客戶端應用復制到您的系統中:
添加配置文件ngrok.cfg:
server_addr: "ngrok.你的域名.com:4443"trust_host_root_certs: false
添加批處理start.bat,如果只運行一次直接在命令行下輸入命令也是一樣的效果,內容如下:
ngrok.exe -subdomain kyt -config=ngrok.cfg 8987
其中8987為端口號,運行成功的結果如下所示:
看到這個界面時說明已成功了。
七、啟動客戶端并測試
打開瀏覽器,輸入您映射后的域名就可以穿透內網訪問您的web服務器了。
八、總結
一開始選擇錯了平臺,在windows花了不少時間,在ubuntu下順利完成。
無論是客戶端還是服務器端最好都做成服務,更方便與穩定。
由于服務器上同時運行著IIS,故服務端Ngrok啟動時無法使用80端口,所以在上面,我使用了801作為Ngrok服務器的http端口,使用IIS的代理功能可以解決這個問題,點擊這里。當然也可以使用nginx將80轉換成其它端口。
許多內容都參考了網友的文章。
如果服務器搭建好了,只運行客戶端穿透內網一分鐘夠了:)。
Si4463無線模塊收發超長數據包
Silicon LabsEZRadioPro 系列的Si4463無線模塊,TX FIFO 和 RX FIFO只有64字節。那么如何實現超過64字節的數據包收發呢?需要繁瑣的去把長包拆分成小于等于64字節的小包,每個小包單獨發送,然后接收端把這些小包數據拼接回原始的長包數據嗎?回答是:不!因為長包的收發在Si446X 收發芯片實現是非常簡單的。
打開WDS軟件或者查看API寄存器描述文檔,有兩個中斷非常有用,就是TX_FIFO_ALMOST_EMPTY_PEND和RX_FIFO_ALMOST_FULL_PEND,這個兩個中斷代表的意思就是TX FIFO 的數據即將發送完,或者RX FIFO即將被填充滿。那么我們只需要根據這兩個中斷,就可以實現超長數據包收發了。
例如,在TX_FIFO_ALMOST_EMPTY_PEND中斷產生時,立刻填充數據到TX FIFO中,新填充的數據會緊跟著之前的數據發送出去,直到數據包完全發送完成,最終產生一個發送完成中斷,表示這包數據發完。接收時也是類似,當收到的數據不斷往RX FIFO中填充,快要填滿的時候,就會產生RX_FIFO_ALMOST_FULL_PEND中斷,這時立刻把RX FIFO讀取出來,那么空出來的FIFO又可以繼續接收新的數據,直到接收到的總數據等于整個完整的數據包長度,產生一個接收完成中斷。整個過程都是連續的,根據包長度和設定的閥值,會產生多個TX_FIFO_ALMOST_EMPTY_PEND和RX_FIFO_ALMOST_FULL_PEND中斷,但是每包數據發送完成和接收完成中斷只會有一個,和短包的收發是一樣的。
WDS的設置界面如下:
設置配置TX_FIFO_ALMOST_EMPTY和RX_FIFO_ALMOST_FULL 閥值
開啟對應的中斷
用戶需要再代碼中添加檢測到TX_FIFO_ALMOST_EMPTY和RX_FIFO_ALMOST_FULL兩種中斷時系統對應的反應。
以上是對Si4463發送超長數據包的一些總結,希望能對你有所幫助。
常見常用的TCP知識點有些啥
/ 計網分層結構 /
考慮最簡單的情況:兩臺主機之間的通信。這個時候只需要一條網線把兩者連起來,規定好彼此的硬件接口,如都用USB、電壓10v、頻率2.4GHz等,這一層就是物理層,這些規定就是物理層協議
。
我們當然不滿足于只有兩臺電腦連接,因此我們可以使用交換機把多個電腦連接起來,如下圖:
這樣連接起來的網絡,稱為局域網,也可以稱為以太網(以太網是局域網的一種)。在這個網絡中,我們需要標識每個機器,這樣才可以指定要和哪個機器通信。這個標識就是硬件地址MAC。硬件地址隨機器的生產就被確定,永久性唯一。在局域網中,我們需要和另外的機器通信時,只需要知道他的硬件地址,交換機就會把我們的消息發送到對應的機器。
這里我們可以不管底層的網線接口如何發送,把物理層抽離,在他之上創建一個新的層次,這就是
數據鏈路層
。
我們依然不滿足于局域網的規模,需要把所有的局域網聯系起來,這個時候就需要用到路由器來連接兩個局域網:
但是如果我們還是使用硬件地址來作為通信對象的唯一標識,那么當網絡規模越來越大,需要記住所有機器的硬件地址是不現實的;同時,一個網絡對象可能會頻繁更換設備,這個時候硬件地址表維護起來更加復雜。這里使用了一個新的地址來標記一個網絡對象:
IP地址 。
通過一個簡單的寄信例子來理解IP地址。
我住在北京市,我朋友A住在上海市,我要給朋友A寫信:
寫完信,我會在信上寫好我朋友A的地址,并放到北京市郵局(給信息附加目標IP地址,并發送給路由器)
郵局會幫我把信運輸到上海市當地郵局(信息會經過路由傳遞到目標IP局域網的路由器)
上海市當地路由器會幫我把信交給朋友A(局域網內通信)
因此,這里IP地址就是一個網絡接入地址(朋友A的住址),我只需要知道目標IP地址,路由器就可以把消息給我帶到。在局域網中,就可以動態維護一個MAC地址與IP地址的映射關系,根據目的IP地址就可以尋找到機器的MAC地址進行發送 。
這樣我們不需管理底層如何去選擇機器,我們只需要知道IP地址,就可以和我們的目標進行通信。這一層就是網絡層。網絡層的核心作用就是 提供主機之間的邏輯通信 。這樣,在網絡中的所有主機,在邏輯上都連接起來了,上層只需要提供目標IP地址和數據,網絡層就可以把消息發送到對應的主機。
一個主機有多個進程,進程之間進行不同的網絡通信,如邊和朋友開黑邊和女朋友聊微信。我的手機同時和兩個不同機器進行通信。那么當我的手機收到數據時,如何區分是微信的數據,還是王者的數據?那么就必須在網絡層之上再添加一層:運輸層
:
運輸層通過socket(套接字),將網絡信息進行進一步的拆分,不同的應用進程可以獨立進行網絡請求,互不干擾。這就是運輸層的最本質特點:
提供進程之間的邏輯通信 。這里的進程可以是主機之間,也可以是同個主機,所以在android中,socket通信也是進程通信的一種方式。
現在不同的機器上的應用進程之間可以獨立通信了,那么我們就可以在計算機網絡上開發出形形式式的應用:如web網頁的http,文件傳輸ftp等等。這一層稱為應用層。
應用層還可以進一步拆分出表示層、會話層,但他們的本質特點都沒有改變:完成具體的業務需求
。和下面的四層相比,他們并不是必須的,可以歸屬到應用層中。
最后對計網分層進行小結:
最底層物理層,負責兩個機器之間通過硬件的直接通信;
數據鏈路層使用硬件地址在局域網中進行尋址,實現局域網通信;
網絡層通過抽象IP地址實現主機之間的邏輯通信;
運輸層在網絡層的基礎上,對數據進行拆分,實現應用進程的獨立網絡通信;
應用層在運輸層的基礎上,根據具體的需求開發形形式式的功能。
這里需要注意的是,分層并不是在物理上的分層,而是邏輯上的分層。通過對底層邏輯的封裝,使得上層的開發可以直接依賴底層的功能而無需理會具體的實現,簡便了開發。
這種分層的思路,也就是責任鏈設計模式,通過層層封裝,把不同的職責獨立起來,更加方便開發、維護等等。okHttp中的攔截器設計模式,也是這種責任鏈模式。
/ 運輸層 /
本文主要是講解TCP,這里需要增加一些運輸層的知識。
本質:提供進程通信
在運輸層之下的網絡層,是不知道該數據包屬于哪個進程,他只負責數據包的接收與發送。運輸層則負責接收不同進程的數據交給網絡層,同時把網絡層的數據拆分交給不同的進程。從上往下匯聚到網絡層,稱為
多路復用,從下往上拆分,稱為多路拆分 。
運輸層的表現,受網絡層的限制。這很好理解,網絡層是運輸層的底層支持。所以運輸層是無法決定自己帶寬、時延等的上限。但可以基于網絡層開發更多的特性:如可靠傳輸。網絡層只負責盡力把數據包從一端發送到另一端,而不保證數據可以到達且完整。
底層實現:socket
前面講到,最簡單的運輸層協議,就是提供進程之間的獨立通信 ,但底層的實現,是socket之間的獨立通信
。在網絡層中,IP地址是一個主機邏輯地址,而在運輸層中,socket是一個進程的邏輯地址;當然,一個進程可以擁有多個socket。應用進程可以通過監聽socket,來獲取這個socket接受到的消息。
socket并不是一個實實在在的東西,而是運輸層抽象出來的一個對象。運輸層增加了
端口這個概念,來區分不同的socket。端口可以理解為一個主機上有很多的網絡通信口,每個端口都有一個端口號,端口的數量由運輸層協議確定。
不同的運輸層協議對socket有不同的定義方式。在UDP協議中,使用目標IP 目標端口號來定義一個socket;在TCP中使用目標IP 目標端口號 源IP 源端口號來定義一個socket。我們只需要在運輸層報文的頭部附加上這些信息,目標主機就會知道我們要發送給哪個socket,對應監聽該socket的進程就可獲得信息。
運輸層協議
運輸層的協議就是大名鼎鼎的TCP和UDP。其中,UDP是最精簡的運輸層協議,只實現了進程間的通信;而TCP在UDP的基礎上,實現了可靠傳輸、流量控制、擁塞控制、面向連接等等特性,同時也更加復雜。
當然除此之外,還有更多更優秀的運輸層協議,但目前廣為使用的,就是TCP和UDP。UDP在后面也會總結到。
/ TCP協議首部 /
TCP協議,表現在報文上,就是會在應用層傳輸下來的數據前附加上一個TCP首部,這個首部附加了TCP信息,先來整體看一下這個首部的結構:
這張圖是來自我大學老師的課件, 非常好用,所以一直拿來學習。最下面部分表示了報文之間的關系,TCP數據部分就是應用層傳下來的數據。
TCP首部固定長度是20字節,下面還有4字節是可選的。內容很多,但其中有一些我們比較熟悉的:源端口,目標端口。嗯?socket不是還需要IP進行定位嗎?IP地址在網絡層被附加了。其他的內容后面都會慢慢講解,作為一篇總結文章,這里放出查閱表,方便復習:
選項字段中包含以下其他選項:
講完下面內容,再回來看這些字段就熟悉了。
/ TCP面向字節流特性 /
TCP并不是把應用層傳輸過來的數據直接加上首部然后發送給目標,而是把數據看成一個字節 流,給他們標上序號之后分部分發送。這就是TCP的 面向字節流
特性:
TCP會以流的形式從應用層讀取數據并存放在自己的發送緩存區中,同時為這些字節標上序號
TCP會從發送方緩沖區選擇適量的字節組成TCP報文,通過網絡層發送給目標
目標會讀取字節并存放在自己的接收方緩沖區中,并在合適的時候交付給應用層
面向字節流的好處是無需一次存儲過大的數據占用太多內存,壞處是無法知道這些字節代表的意義,例如應用層發送一個音頻文件和一個文本文件,對于TCP來說就是一串字節流,沒有意義可言,這會導致粘包以及拆包問題,后面講。
/ 可靠傳輸原理 /
前面講到,TCP是可靠傳輸協議,也就是,一個數據交給他,他肯定可以完整無誤地發送到目標地址,除非網絡炸了。他實現的網絡模型如下:
對于應用層來說,他就是一個可靠傳輸的底層支持服務;而運輸層底層采用了網絡層的不可靠傳輸。雖然在網絡層甚至數據鏈路層就可以使用協議來保證數據傳輸的可靠性,但這樣網絡的設計會更加復雜、效率會隨之降低。把數據傳輸的可靠性保證放在運輸層,會更加合適。
可靠傳輸原理的重點總結一下有:
滑動窗口、超時重傳、累積確認、選擇確認、連續ARQ 。
停止等待協議
要實現可靠傳輸,最簡便的方法就是:我發送一個數據包給你,然后你跟我回復收到,我繼續發送下一個數據包。傳輸模型如下:
這種“一來一去”的方法來保證傳輸可靠就是
停止等待協議(stop-and-wait)。不知道還記不記得前面TCP首部有一個ACK字段,當他設置為1的時候,表示這個報文是一個確認收到報文。
然后再來考慮一種情況:丟包。網絡環境不可靠,導致每一次發送的數據包可能會丟失,如果機器A發送了數據包丟失了,那么機器B永遠接收不到數據,機器A永遠在等待。解決這個問題的方法是:超時重傳 。當機器A發出一個數據包時便開始計時,時間到還沒收到確認回復,就可以認為是發生了丟包,便再次發送,也就是重傳。
但重傳會導致另一種問題:如果原先的數據包并沒有丟失,只是在網絡中待的時間比較久,這個時候機器B會受到兩個數據包,那么機器B是如何辨別這兩個數據包是屬于同一份數據還是不同的數據?這就需要前面講過的方法:給數據字節進行編號。這樣接收方就可以根據數據的字節編號,得出這些數據是接下來的數據,還是重傳的數據。
在TCP首部有兩個字段:序號和確認號,他們表示發送方數據第一個字節的編號,和接收方期待的下一份數據的第一個字節的編號。前面講到TCP是面向字節流,但是他并不是一個字節一個字節地發送,而是一次截取一整段。截取的長度受多種因素影響,如緩存區的數據大小、數據鏈路層限制的幀大小等。
連續ARQ協議
停止等待協議已經可以滿足可靠傳輸了,但有一個致命缺點:效率太低。發送方發送一個數據包之后便進入等待,這個期間并沒有干任何事,浪費了資源。解決的方法是:連續發送數據包
。模型如下:
和停止等待最大的不同就是,他會源源不斷地發送,接收方源源不斷收到數據之后,逐一進行確認回復。這樣便極大地提高了效率。但同樣,帶來了一些額外的問題:
發送是否可以無限發送直到把緩沖區所有數據發送完?不可以。因為需要考慮接收方緩沖區以及讀取數據的能力。如果發送太快導致接收方無法接受,那么只是會頻繁進行重傳,浪費了網絡資源。所以發送方發送數據的范圍,需要考慮到接收方緩沖區的情況。這就是TCP的
流量控制 。解決方法是:滑動窗口
。基本模型如下:
發送方需要根據接收方的緩沖區大小,設置自己的可發送窗口大小,處于窗口內的數據表示可發送,之外的數據不可發送。
當窗口內的數據接收到確認回復時,整個窗口會往前移動,直到發送完成所有的數據
在TCP的首部有一個窗口大小字段,他表示接收方的剩余緩沖區大小,讓發送方可以調整自己的發送窗口大小。通過滑動窗口,就可以實現TCP的流量控制,不至于發送太快,導致太多的數據丟失。
連續ARQ帶來的第二個問題是:網絡中充斥著和發送數據包一樣數據量的確認回復報文,因為每一個發送數據包,必須得有一個確認回復。提高網絡效率的方法是:累積確認 。接收方不需要逐個進行回復,而是累積到一定量的數據包之后,告訴發送方,在此數據包之前的數據全都收到。例如,收到 1234,接收方只需要告訴發送方我收到4了,那么發送方就知道1234都收到了。
第三個問題是:如何處理丟包情況。在停止等待協議中很簡單,直接一個超時重傳就解決了。但,連續ARQ中不太一樣。例如:接收方收到了消息 123 567,六個字節,編號為4的字節丟失了。按照累積確認的思路,只能發送3的確認回復,567都必須丟掉,因為發送方會進行重傳。這就是GBN(go-back-n) 思路。
但是我們會發現,只需要重傳4即可,這樣不是很浪費資源,所以就有了:選擇確認SACK 。在TCP報文的選項字段,可以設置已經收到的報文段,每一個報文段需要兩個邊界來進行確定。這樣發送方,就可以根據這個選項字段重傳丟失的數據了。
可靠傳輸小結
到這里關于TCP的可靠傳輸原理就已經介紹的差不多。最后進行一個小結:
通過連續ARQ協議與發送-確認回復模式來保證每一個數據包都到達接收方
通過給字節編號的方法,來標記每一個數據是屬于重傳還是新的數據
通過超時重傳的方式,來解決數據包在網絡中丟失的問題
通過滑動窗口來實現流量控制
通過累積確認 選擇確認的方法來提高確認回復與重傳的效率
當然,這只是可靠傳輸的冰山一角,感興趣的可以再深入去研究(和面試官聊天已經差不多了[狗頭])。/ 擁塞控制 /
擁塞控制考慮的是另外一個問題:避免網絡過分擁擠導致丟包嚴重,網絡效率降低 。
拿現實的交通舉例子:
高速公路同一時間可通行的汽車數量是一定的,當節假日時,就會發生嚴重的堵車。在TCP中,數據包超時,會進行重傳,也就是會進來更多的汽車,這時候更堵,最后導致的結果就是:丟包-重傳-丟包-重傳。最后整個網絡癱瘓了。
這里的擁塞控制和前面的流量控制不是一個東西,流量控制是擁塞控制的手段:為了避免擁塞,必須對流量進行控制。擁塞控制目的是:限制每個主機的發送的數據量,避免網絡擁塞效率下降。就像廣州等地,限制車牌號出行是一個道理。不然大家都堵在路上,誰都別想走。
擁塞控制的解決方法是流量控制,流量控制的實現是滑動窗口,所以擁塞控制最終也是通過限制發送方的滑動窗口大小來限制流量 。當然,擁塞控制的手段不只是流量控制,導致擁塞的因素有:路由器緩存、帶寬、處理器處理速度等等。提升硬件能力(把4車道改成8車道)是其中一個方法,但畢竟硬件提升是有瓶頸的,沒辦法不斷提升,還是需要從tcp本身來增加算法,解決擁塞。
擁塞控制的重點有4個:慢開始、快恢復、快重傳、擁塞避免
。這里依舊獻祭出大學老師的ppt圖片:
Y軸表示的是發送方窗口大小,X軸表示的是發送的輪次(不是字節編號)。
最開始的時候,會把窗口設置一個較小的值,然后每輪變為原來的兩倍。這是慢開始。
當窗口值到達ssthresh值,這個值是需要通過實時網絡情況設置的一個窗口限制值,開始進入擁塞避免,每輪把窗口值提升1,慢慢試探網絡的底線。
如果發生了數據超時,表示極可能發生了擁塞,然后回到慢開始,重復上面的步驟。
如果收到三個相同的確認回復,表示現在網絡的情況不太好,把ssthresh的值設置為原來的一半,繼續擁塞避免。這部分稱為快恢復。
如果收到丟包信息,應該盡快把丟失的包重傳一次,這是快重傳。
當然,窗口的最終上限是不能無限上漲的,他不能超過接收方的緩存區大小。
通過這個算法,就可以在很大程度上,避免網絡擁擠。
除此之外,還可以讓路由器在緩存即將滿的時候,告知發送方我快滿了,而不是等到出現了超時再進行處理,這是主動隊列管理AQM。此外還有很多方法,但是上面的算法是重點。
/ 面向連接 /
這一小節講的就是無人不曉的TCP三次握手與四次揮手這些,經過前面的內容,這一小節其實已經很好理解。
TCP是面向連接的,那連接是什么?這里的連接并不是實實在在的連接,而是通信雙方彼此
之間的一個記錄 。TCP是一個全雙工通信,也就是可以互相發送數據,所以雙方都需要記錄對方的信息。根據前面的可靠傳輸原理,TCP通信雙方需要為對方準備一個接收緩沖區可以接收對方的數據、記住對方的socket知道怎么發送數據、記住對方的緩沖區來調整自己的窗口大小等等,這些記錄,就是一個連接。
在運輸層小節中講到,運輸層雙方通信的地址是采用socket來定義的,TCP也不例外。TCP的每一個連接只能有兩個對象,也就是兩個socket,而不能有三個。所以socket的定義需要源IP、源端口號、目標IP、目標端口號四個關鍵因素,才不會發生混亂。
假如TCP和UDP一樣只采用目標IP 目標端口號來定義socket,那么就會出現多個發送方同時發送到同一個目標socket的情況。這個時候TCP無法區分這些數據是否來自不同的發送方,就會導致出現錯誤。
既然是連接,就有兩個關鍵要點:建立連接、斷開連接。
建立連接
建立連接的目的就是交換彼此的信息,然后記住對方的信息。所以雙方都需要發送彼此的信息給對方:
但前面的可靠傳輸原理告訴我們,數據在網絡中傳輸是不可靠的,需要對方給予我們一個確認回復,才可以保證消息正確到達。如下圖:
機器B的確認收到和機器B信息可以進行合并,減少次數;而且發送機器B給機器A本身就代表了機器B已經收到了消息,所以最后的示例圖是:
步驟如下:
機器A發送syn包向機器B請求建立TCP連接,并附加上自身的接收緩沖區信息等,機器A進入SYN_SEND狀態,表示請求已經發送正在等待回復;
機器B收到請求之后,根據機器A的信息記錄下來,并創建自身的接收緩存區,向機器A發送syn ack的合成包,同時自身進入SYN_RECV狀態,表示已經準備好了,等待機器A 的回復就可以向A發送數據;
機器A收到回復之后記錄機器B 的信息,發送ack信息,自身進入ESTABLISHED狀態,表示已經完全準備好了,可以進行發送和接收;
機器B收到ACK數據之后,進入ESTABLISHED狀態。
三次消息的發送,稱為三次握手。
斷開連接
斷開連接和三次握手類似,直接上圖:
1. 機器A發送完數據之后,向機器B請求斷開連接,自身進入FIN_WAIT_1狀態,表示數據發送完成且已經發送FIN包(FIN標志位為1);
2. 機器B收到FIN包之后,回復ack包表示已經收到,但此時機器B可能還有數據沒發送完成,自身進入CLOSE_WAIT狀態,表示對方已發送完成且請求關閉連接,自身發送完成之后可以關閉連接;
3. 機器B數據發送完成之后,發送FIN包給機器B ,自身進入LAST_ACK狀態,表示等待一個ACK包即可關閉連接;
4. 機器A收到FIN包之后,知道機器B也發送完成了,回復一個ACK包,并進入TIME_WAIT狀態
TIME_WAIT狀態比較特殊。當機器A收到機器B的FIN包時,理想狀態下,確實是可以直接關閉連接了;但是:
我們知道網絡是不穩定的,可能機器B 發送了一些數據還沒到達(比FIN包慢);
同時回復的ACK包可能丟失了,機器B會重傳FIN包;
如果此時機器A馬上關閉連接,會導致數據不完整、機器B無法釋放連接等問題。所以此時機器A需要等待2個報文生存最大時長,確保網絡中沒有任何遺留報文了,再關閉連接
5. 最后,機器A等待兩個報文存活最大時長之后,機器B 接收到ACK報文之后,均關閉連接,進入CLASED狀態
雙方之間4次互相發送報文來斷開連接的過程,就是四次揮手。
現在,對于為什么握手是三次揮手是四次、一定要三次/四次嗎、為什么要停留2msl再關閉連接等等這些問題,就都解決了。
/ UDP協議 /
運輸層協議除了TCP,還有大名鼎鼎的UDP。如果說TCP憑借他完善穩定的功能獨樹一幟,那UDP就是精簡主義亂拳打死老師傅。
UDP只實現了運輸層最少的功能:進程間通信。對于應用層傳下來的數據,UDP只是附加一個首部就直接交給網絡層了。UDP的頭部非常簡單,只有三部分:
源端口、目標端口:端口號用來區分主機的不同進程
校驗碼:用于校驗數據包在傳輸的過程中沒有出現錯誤,例如某個1變成了0
長度:報文的長度
所以UDP的功能也只有兩個:校驗數據報是否發生錯誤、區分不同的進程通信。但,TCP的功能雖然多,但同時也是要付出相對應的代價。例如面向連接的特性,在建立和斷開連接的時候會有開銷;擁塞控制的特性,會限制傳輸的上限等等。下面來羅列一下UDP的優缺點:
UDP的缺點
無法保證消息完整、正確到達,UDP是一個不可靠的傳輸協議;
缺少擁塞控制容易互相競爭資源導致網絡系統癱瘓
UDP的優點
效率更快;不需要建立連接以及擁塞控制
連接更多的客戶;沒有連接狀態,不需要為每個客戶創建緩存等
分組首部字節少,開銷小;TCP首部固定首部是20字節,而UDP只有8字節;更小的首部意味著更大比例的數據部分
在一些需要高效率允許可限度誤差的場景下可以使用。如直播場景,并不需要保證每個數據包都完整到達,允許一定的丟包率,這個時候TCP的可靠特性反而成為了累贅;精簡的UDP更高的效率是更加適合的選擇
可以進行廣播;UDP并不是面向連接的,所以可以同時對多個進程進行發送報文
UDP適用場景
UDP適用于對傳輸模型需要應用層高度自定義、允許出現丟包、需要高效率的場景、需要廣播;例如
視屏直播
DNS
RIP路由選擇協議
/ 其他補充 /
分塊傳輸
我們可以發現,運輸層在傳輸數據的時候,并不是把整個數據包加個首部直接發送過去,而是會拆分成多個報文分開發送;那他這樣做原因是什么?
有讀者可能會想到:數據鏈路層限制了數據長度只能有1460。那數據鏈路層為什么要這么限制?他的本質原因就是:網絡是不穩定的。如果報文太長,那么極有可能在傳輸一般的時候突然中斷了,這個時候就要整個數據重傳,效率就降低了。把數據拆分成多個數據報,那么當某個數據報丟失,只需要重傳該數據報即可。
那是不是拆分得越細越好?報文中數據字段長度太低,會使得首部的占比太大,這樣首部就會成為網絡傳輸最大的負擔了。例如1000字節,每個報文首部是40字節,如果拆分成10個報文,那么只需要傳輸400字節的首部;而如果拆分成1000個,那么需要傳輸40000字節的首部,效率就極大地降低了。
路由轉換
先看下圖:
正常情況下,主機A的數據包可以又 1-3-6-7路徑進行傳送
如果路由3壞掉了,那么可以從 1-4-6-7進行傳送
如果4也壞掉了,那么只能從2-5-6-7傳送
如果5壞掉了,那么就中斷線路了
可以看出來,使用路由轉發的好處是:提高網絡的容錯率,本質原因依舊是網絡是不穩定的 。即使壞掉幾個路由器,網絡依舊暢通。但是如果壞掉路由器6那就直接導致主機A和主機B無法通信,所以要避免這種核心路由器的存在。
使用路由的好處還有:分流。如果一條線路太擁堵,可以從別的路線進行傳輸,提高效率。
粘包與拆包
在面向字節流那一小節講過,TCP不懂這些數據流的意義,他只知道從應用層拿到數據流,切割成一份份報文,然后發送給目標對象。而如果應用層傳輸下來的是兩個數據包,那么極有可能出現這種情況:
應用層需要向目標進程發送兩份數據,一份音頻,一份文本
TCP只知道接收到一個流,并把流拆分成4段進行發送
中間第二個報文的數據就出現兩個文件的數據混在一起,這就是粘包
目標進程應用層在接收到數據之后,需要把這些數據拆分成正確的兩個文件,就是拆包
粘包與拆包都是應用層需要解決的問題,可以在每個文件的最后附加上一些特殊的字節,如換行符;或者控制每個報文只包含一個文件的數據,不足的用0補充等等。
惡意攻擊
TCP的面向連接特點可能會被惡意的人利用,對服務器進行攻擊。
前面我們知道,當我們向一個主機發送syn包請求創建連接時,服務器會為我們創建緩沖區等,然后向我們返回syn ack報文;如果我們偽造IP和端口,向一個服務器進行海量的請求,會使得服務器創建了大量的創建一半的TCP連接,使得其無法正常響應用戶的請求,導致服務器癱瘓。
解決的方法可以有限制IP的創建連接數、讓創建一半的tcp連接在更短的時間內自行關閉、延緩接收緩沖區內存的分配等等。
長連接
我們向服務器的每一次請求都需要創建一個TCP連接,服務器返回數據之后就會關閉連接;如果在短時間內有大量的請求,那么頻繁創建TCP連接關閉TCP連接是一個很浪費資源的行為。所以我們可以讓TCP連接不要關閉,在這個期間進行請求,提高效率。需要注意長連接維持時間、創建條件等,避免被惡意利用創建大量的長連接,消耗殆盡服務器的資源。
/ 最后 /
以前學習的時候覺得這些東西好像沒什么卵用,貌似就是用來考試的。事實上,在沒應用到的時候,對這些知識很難有更深層次的認知,例如現在我看上面的總結,很多只是表面上的認知,不知道他背后代表的真正含義。
但當我學習得更加廣泛、深入,會對這些知識有越來越深刻的認識。有那么幾個瞬間覺得:哦原來那個東西是這樣運用的,那個東西是這樣的啊,原來學了是真的有用。
現在可能學了之后沒有什么感覺,但是當用到或者學到相關的應用時,會有一個頓悟感,會瞬間收獲很多。
1
來源:https://juejin.cn/user/3931509313252552/posts