1. <dd id="erndk"></dd>
                1. Android系統自帶的VPN服務框架實例詳解

                  2019/7/7 20:32:32

                  這篇文章主要介紹了Android系統自帶的VPN服務框架實例詳解的相關資料,需要的朋友可以參考下

                  Android系統自帶的VPN服務框架

                  Android從4.0開始(API LEVEL 15),自己帶了一個幫助在設備上建立VPN連接的解決方案,且不需要root權限,本文將對其做一個簡單的介紹。

                  一、基本原理

                  在介紹如何使用這些新增的API之前,先來說說其基本的原理。

                  android設備上,如果已經使用了VpnService框架,建立起了一條從設備到遠端的VPN鏈接,那么數據包在設備上大致經歷了如下四個過程的轉換:

                  1)應用程序使用socket,將相應的數據包發送到真實的網絡設備上。一般移動設備只有無線網卡,因此是發送到真實的WiFi設備上;

                  2)Android系統通過iptables,使用NAT,將所有的數據包轉發到TUN虛擬網絡設備上去,端口是tun0;

                  3)VPN程序通過打開/dev/tun設備,并讀取該設備上的數據,可以獲得所有轉發到TUN虛擬網絡設備上的IP包。因為設備上的所有IP包都會被NAT轉成原地址是tun0端口發送的,所以也就是說你的VPN程序可以獲得進出該設備的幾乎所有的數據(也有例外,不是全部,比如回環數據就無法獲得);

                  4)VPN數據可以做一些處理,然后將處理過后的數據包,通過真實的網絡設備發送出去。為了防止發送的數據包再被轉到TUN虛擬網絡設備上,VPN程序所使用的socket必須先被明確綁定到真實的網絡設備上去。

                  二、代碼實現

                  要實現Android設備上的VPN程序,一般需要分別實現一個繼承自Activity類的帶UI的客戶程序和一個繼承自VpnService類的服務程序。

                  申明權限

                  要想讓你的VPN程序正常運行,首先必須要在AndroidManifest.xml中顯式申明使用“android.permission.BIND_VPN_SERVICE”權限。

                  客戶程序實現

                  客戶程序一般要首先調用VpnService.prepare函數:

                  Intent intent = VpnService.prepare(this); 
                  if (intent != null) { 
                    startActivityForResult(intent, 0); 
                  } else { 
                    onActivityResult(0, RESULT_OK, null); 
                  } 
                  

                  目前Android只支持一條VPN連接,如果新的程序想建立一條VPN連接,必須先中斷系統中當前存在的那個VPN連接。

                  同時,由于VPN程序的權利實在太大了,所以在正式建立之前,還要彈出一個對話框,讓用戶點頭確認。

                  VpnService.prepare函數的目的,主要是用來檢查當前系統中是不是已經存在一個VPN連接了,如果有了的話,是不是就是本程序創建的。

                  如果當前系統中沒有VPN連接,或者存在的VPN連接不是本程序建立的,則VpnService.prepare函數會返回一個intent。這個intent就是用來觸發確認對話框的,程序會接著調用startActivityForResult將對話框彈出來等用戶確認。如果用戶確認了,則會關閉前面已經建立的VPN連接,并重置虛擬端口。該對話框返回的時候,會調用onActivityResult函數,并告之用戶的選擇。

                  如果當前系統中有VPN連接,并且這個連接就是本程序建立的,則函數會返回null,就不需要用戶再確認了。因為用戶在本程序第一次建立VPN連接的時候已經確認過了,就不要再重復確認了,直接手動調用onActivityResult函數就行了。

                  在onActivityResult函數中,處理相對簡單:

                  protected void onActivityResult(int request, int result, Intent data) { 
                    if (result == RESULT_OK) { 
                      Intent intent = new Intent(this, MyVpnService.class); 
                      ... 
                      startService(intent); 
                    } 
                  } 
                  

                  如果返回結果是OK的,也就是用戶同意建立VPN連接,則將你寫的,繼承自VpnService類的服務啟動起來就行了。

                  當然,你也可以通過intent傳遞一些別的參數。

                  服務程序實現

                  服務程序必須要繼承自android.NET.VpnService類:

                  public class MyVpnService extends VpnService ... 

                  VpnService類封裝了建立VPN連接所必須的所有函數,后面會逐步用到。

                  建立鏈接的第一步是要用合適的參數,創建并初始化好tun0虛擬網絡端口,這可以通過在VpnService類中的一個內部類Builder來做到:

                  Builder builder = new Builder(); 
                  builder.setMtu(...); 
                  builder.addAddress(...); 
                  builder.addRoute(...); 
                  builder.addDnsServer(...); 
                  builder.addSearchDomain(...); 
                  builder.setSession(...); 
                  builder.setConfigureIntent(...); 
                   
                  ParcelFileDescriptor interface = builder.establish(); 
                  

                  可以看到,這里使用了標準的Builder設計模式。在正式建立(establish)虛擬網絡接口之前,需要設置好幾個參數,分別是:

                  1)MTU(Maximun Transmission Unit),即表示虛擬網絡端口的最大傳輸單元,如果發送的包長度超過這個數字,則會被分包;

                  2)Address,即這個虛擬網絡端口的IP地址;

                  3)Route,只有匹配上的IP包,才會被路由到虛擬端口上去。如果是0.0.0.0/0的話,則會將所有的IP包都路由到虛擬端口上去;

                  4)DNS Server,就是該端口的DNS服務器地址;

                  5)Search Domain,就是添加DNS域名的自動補齊。DNS服務器必須通過全域名進行搜索,但每次查找都輸入全域名太麻煩了,可以通過配置域名的自動補齊規則予以簡化;

                  6)Session,就是你要建立的VPN連接的名字,它將會在系統管理的與VPN連接相關的通知欄和對話框中顯示出來;


                  7)Configure Intent,這個intent指向一個配置頁面,用來配置VPN鏈接。它不是必須的,如果沒設置的話,則系統彈出的VPN相關對話框中不會出現配置按鈕。

                  最后調用Builder.establish函數,如果一切正常的話,tun0虛擬網絡接口就建立完成了。并且,同時還會通過iptables命令,修改NAT表,將所有數據轉發到tun0接口上。

                  這之后,就可以通過讀寫VpnService.Builder返回的ParcelFileDescriptor實例來獲得設備上所有向外發送的IP數據包和返回處理過后的IP數據包到TCP/IP協議棧:

                  // Packets to be sent are queued in this input stream. 
                  FileInputStream in = new FileInputStream(interface.getFileDescriptor()); 
                   
                  // Packets received need to be written to this output stream. 
                  FileOutputStream out = new FileOutputStream(interface.getFileDescriptor()); 
                   
                  // Allocate the buffer for a single packet. 
                  ByteBuffer packet = ByteBuffer.allocate(32767); 
                  ... 
                  // Read packets sending to this interface 
                  int length = in.read(packet.array()); 
                  ... 
                  // Write response packets back 
                  out.write(packet.array(), 0, length); 
                  

                  ParcelFileDescriptor類有一個getFileDescriptor函數,其會返回一個文件描述符,這樣就可以將對接口的讀寫操作轉換成對文件的讀寫操作。

                  每次調用FileInputStream.read函數會讀取一個IP數據包,而調用FileOutputStream.write函數會寫入一個IP數據包到TCP/IP協議棧。

                  這其實基本上就是這個所謂的VpnService的全部了,是不是覺得有點奇怪,半點沒涉及到建立VPN鏈接的事情。這個框架其實只是可以讓某個應用程序可以方便的截獲設備上所有發送出去和接收到的數據包,僅此而已。能獲得這些數據包,當然可以非常方便的將它們封裝起來,和遠端VPN服務器建立VPN鏈接,但是這一塊VpnService框架并沒有涉及,留給你的應用程序自己解決。

                  還有一點要特別解釋一下,一般的應用程序,在獲得這些IP數據包后,會將它們再通過socket發送出去。但是,這樣做會有問題,你的程序建立的socket和別的程序建立的socket其實沒有區別,發送出去后,還是會被轉發到tun0接口,再回到你的程序,這樣就是一個死循環了。為了解決這個問題,VpnService類提供了一個叫protect的函數,在VPN程序自己建立socket之后,必須要對其進行保護:

                  protect(my_socket); 

                  其背后的原理是將這個socket和真實的網絡接口進行綁定,保證通過這個socket發送出去的數據包一定是通過真實的網絡接口發送出去的,不會被轉發到虛擬的tun0接口上去。

                  好了,Android系統默認提供的這個VPN框架就只有這么點東西。

                  最后,簡單總結一下:

                  1)VPN連接對于應用程序來說是完全透明的,應用程序完全感知不到VPN的存在,也不需要為支持VPN做任何更改;

                  2)并不需要獲得Android設備的root權限就可以建立VPN連接。你所需要的只是在你應用程序內的AndroidManifest.xml文件中申明需要一個叫做“android.permission.BIND_VPN_SERVICE”的特殊權限;

                  3)在正式建立VPN鏈接之前,Android系統會彈出一個對話框,需要用戶明確的同意;

                  4)一旦建立起了VPN連接,Android設備上所有發送出去的IP包,都會被轉發到虛擬網卡的網絡接口上去(主要是通過給不同的套接字打fwmark標簽和iproute2策略路由來實現的);

                  5)VPN程序可以通過讀取這個接口上的數據,來獲得所有設備上發送出去的IP包;同時,可以通過寫入數據到這個接口上,將任何IP數據包插入系統的TCP/IP協議棧,最終送給接收的應用程序;

                  6)Android系統中同一時間只允許建立一條VPN鏈接。如果有程序想建立新的VPN鏈接,在獲得用戶同意后,前面已有的VPN鏈接會被中斷;

                  7)這個框架雖然叫做VpnService,但其實只是讓程序可以獲得設備上的所有IP數據包。通過前面的簡單分析,大家應該已經感覺到了,這個所謂的VPN服務,的確可以方便的用來在Android設備上建立和遠端服務器之間的VPN連接,但其實它也可以被用來干很多有趣的事情,比如可以用來做防火墻,也可以用來抓設備上的所有IP包。

                  感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

                  隨時隨地學軟件編程-關注百度小程序和微信小程序
                  關于找一找教程網

                  本站文章僅代表作者觀點,不代表本站立場,所有文章非營利性免費分享。
                  本站提供了軟件編程、網站開發技術、服務器運維、人工智能等等IT技術文章,希望廣大程序員努力學習,讓我們用科技改變世界。
                  [Android系統自帶的VPN服務框架實例詳解]http://www.yachtsalesaustralia.com/tech/detail-57627.html

                  贊(0)
                  關注微信小程序
                  程序員編程王-隨時隨地學編程

                  掃描二維碼或查找【程序員編程王】

                  可以隨時隨地學編程啦!

                  技術文章導航 更多>
                  国产在线拍揄自揄视频菠萝

                        1. <dd id="erndk"></dd>