作者:琪米 時間: 2018.8.02

引言: 最近就要離職了,總覺得不把自己做過的技術分享出來是一種遺憾。而且我認為技術要分享才能促進世界的進步(不好意思裝了逼了),我懷著這個偉大的理想就開始了這個系列的文章。廢話講完下面開始講正事。
我相信同是程式設計師很多人應該都對Google搜尋情有獨鍾,奈何國內的牆太過厲害。阻礙了很多人的夢想,此時會有一批有志之士發揮了智慧建造了通向外面世界的梯子。這個梯子是怎麼實現的呢?繼續往下看吧!!
Google爸爸在4.0之後的Android系統放出一個強大的一個服務,叫做VpnServer,它位於android SDK的android.net的包下面。通過這個服務我們可以實現在應用層通過配置一些引數來開啟底層luinx核心的tun網路卡,通過這個tun網路卡能夠實現對android手機上的ip層的網路資料進行讀寫操作。
繼承VpnServer服務
要使用這個服務,不能夠直接使用,要先對這個服務進行繼承。
public class DemoVpnServer extends VpnService
複製程式碼
配置基本引數建立一個底層的tun虛擬網路卡
private ParcelFileDescriptor openTun()
{
Builder builder = new Builder();
//設定一個網路卡的名字
builder.setSession("VpnDemo");
//設定ip資料包長度大小
builder.setMtu(1500);
//設定虛擬網路卡ip地址
builder.addAddress("10.0.0.11",32);
//設定路由地址 0.0.0.0/0表示所有網路資料路由到虛擬網路卡上面去 ps:可以設定多個
builder.addRoute("0.0.0.0",0);
//設定這個虛擬網路卡的dns地址 ps:可以設定多個
builder.addDnsServer("8.8.8.8");
//建立虛擬網路卡返回一個讀取網路卡資料的檔案描述符
return builder.establish();
}
複製程式碼
開啟了一個網路卡之後,程式就可以直接讀取底層網路卡的ip資料包了。 具體程式碼如下:
public class DemoVpnServer extends VpnService implements Runnable{
private Thread mVpnRunThread;
private AtomicBoolean mVpnThreadRun;
private ParcelFileDescriptor openTun()
{
Builder builder = new Builder();
//設定一個網路卡的名字
builder.setSession("VpnDemo");
//設定ip資料包長度大小
builder.setMtu(1500);
//設定虛擬網路卡ip地址
builder.addAddress("10.0.0.11",32);
//設定路由地址 0.0.0.0/0表示所有網路資料路由到虛擬網路卡上面去 ps:可以設定多個
builder.addRoute("0.0.0.0",0);
//設定這個虛擬網路卡的dns地址 ps:可以設定多個
builder.addDnsServer("8.8.8.8");
//建立虛擬網路卡返回一個讀取網路卡資料的檔案描述符
return builder.establish();
}
public DemoVpnServer()
{
mVpnRunThread = new Thread(this);
mVpnThreadRun = new AtomicBoolean();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mVpnRunThread.start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
mVpnThreadRun.set(false);
}
@Override
public void run() {
mVpnThreadRun.set(true);
ParcelFileDescriptor fd = openTun();
FileOutputStream fileOutputStream = new FileOutputStream(fd.getFileDescriptor());
FileInputStream fileInputStream = new FileInputStream(fd.getFileDescriptor());
byte[] buffer = new byte[1600];
while (mVpnThreadRun.get())
{
try {
int length = fileInputStream.read(buffer);
if(length <= 0)
Thread.sleep(50);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
//關閉底層虛擬網路卡
fd.close();
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
複製程式碼
由於在虛擬網路卡哪裡配置的路由是0.0.0.0/0所以所有通過手機物理網路卡得網路資料包都會路由到程式建立得tun虛擬網路卡中去,然後從builder.establish();返回得檔案描述符中可以把網路卡得資料讀出來,讀得操作在這裡

雖然在配置網路卡資料得時候設定得ip資料包大小為1500,事實上偶然還是會有一些資料包得長度會超出1500,但是超出得幅度不大,所以我給讀取每個buffer的時候大小是1600。而且讀出來的長度也不一定是1500,這點需要自己根據返回的長度進行處理,ps:int length = fileInputStream.read(buffer)。最後還要在AndroidManifest.xml加上:
<service android:name=".DemoVpnServer"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
複製程式碼
下面執行一下程式看下前後的ip情況: vpnServer還沒跑起來的情況:


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = VpnService.prepare(this);
if(intent!=null)
startActivityForResult(intent,1);
else
startService(new Intent(this,DemoVpnServer.class));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1 && resultCode == RESULT_OK)
startService(new Intent(this,DemoVpnServer.class));
}
複製程式碼
啟動一個VpnServer大致的流程圖如下:

至此就完成了接管手機上網路資料的要求了。但是接管了手機的網路我們能幹些什麼呢?其實使用場景還是有不少地方,例如比較熟悉的VPN翻牆,代理翻牆,網路捉包工具,網路防火牆,廣告攔截器等都可以使用VpnServer來實現。(如有轉發請註明出處哦!!!)