• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 连接VPN
2<!--Kit: Network Kit-->
3<!--Subsystem: Communication-->
4<!--Owner: @wmyao_mm-->
5<!--Designer: @guo-min_net-->
6<!--Tester: @tongxilin-->
7<!--Adviser: @zhang_yixin13-->
8
9## 简介
10
11VPN,即虚拟专用网络(Virtual Private Network),是在公用网络上建立专用网络的一种技术。在VPN网络中,任意两个节点间的连接并非依赖传统专用网络所需要的端到端的物理链路,而是构建在公用网络服务商提供的平台(如Internet)之上的逻辑网络。用户数据在这一逻辑链路中进行传输。
12
13OpenHarmony为开发者提供了用于创建VPN的API解决方案。当前提供三方VPN能力主要用于创建虚拟网卡及配置VPN路由信息,连接隧道过程及内部连接的协议需要应用内部自行实现。本文将指导您如何开发自己的VPN客户端。
14
15> **说明:**
16>
17>- 为了保证应用的运行效率,所有API调用都是异步的,对于异步调用的API均提供了Promise的方式,以下示例均采用Promise方式,更多方式可以查阅[API参考](../reference/apis-network-kit/js-apis-net-vpnExtension.md)。
18>- 完整的JS API说明以及示例代码请参考:[VPN扩展应用API](../reference/apis-network-kit/js-apis-net-vpnExtension.md)。
19>- 使用该功能需要[ohos.permission.INTERNET](../security/AccessToken/permissions-for-all.md#ohospermissioninternet)权限。
20
21## VPN应用的显示体验
22
23借助系统提供的VPN Extension接口开发者可以构建支持不同协议的VPN服务。OpenHarmony系统提供了界面 (UI) 使用户可以了解当前VPN应用服务的启动和连接:
24
25- 在VPN应用首次启动连接之前,系统会显示VPN连接授权对话框。该对话框会提示用户是否信任该VPN应用并接受VPN连接请求。
26- 当VPN启动连接成功时,状态栏显示一个VPN (钥匙) 图标以提醒用户VPN处于连接状态。
27
28为了使用户可以方便的查看和配置,您的VPN应用还需要提供以下界面:
29
30- 用于手动启动和停止连接的控件。
31- 当VPN启动连接时,在通知栏显示VPN应用的连接状态或提供网络统计信息 (如连接时长、流量等) 。点击该通知能够将您的VPN应用调入前台。
32
33
34## 开发步骤
35
36### 创建VPN Extension Ability
37
38如果想使您的应用支持VPN能力,首先您需要创建一个继承于VpnExtensionAbility的extensionAbilities。
39
40```ts
41// 举例:在应用的module.json5中定义MyVpnExtAbility。
42"extensionAbilities": [
43  {
44    "name": "MyVpnExtAbility",
45    "description": "vpnservice",
46    "type": "vpn",
47    "srcEntry": "./ets/serviceextability/MyVpnExtAbility.ts"
48  }
49]
50```
51
52> **注意:**
53>
54> 如果DevEco Studio工具提示不能识别"type": "vpn",需要您手动在SDK的toolchains\modulecheck\module.json文件中,给extensionAbilities对应的type枚举添加"vpn"定义,并清除build缓存。
55
56接下来您需要在创建的VpnExtensionAbility中实现VPN的配置、启动和停止操作:
57
58- 建立一个VPN的网络隧道,以UDP隧道为例(参考本文下方VPN Demo示例工程文件[napi_init](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/napi_init.cpp)的UdpConnect()方法);
59- 通过VpnConnection.[protect](../reference/apis-network-kit/js-apis-net-vpnExtension.md#protect)保护前一步建立的UDP隧道;
60- 构建VPN Config参数,参考[VPN Config参数说明](#vpn-config参数说明);
61- 通过VpnConnection.[create](../reference/apis-network-kit/js-apis-net-vpnExtension.md#create)建立VPN网络连接;
62- 处理虚拟网卡的数据,如:读写操作。
63
64### 启动VPN Extension Ability
65
66当VPN应用启动VPN连接时,需要调用startVpnExtensionAbility接口,携带需要启动的VpnExtensionAbility信息,其中bundleName需要与您的VPN应用bundleName一致,abilityName为您在前面创建的VpnExtensionAbility名。您可参考如下示例:
67
68```ts
69import { common, Want } from '@kit.AbilityKit';
70import { vpnExtension } from '@kit.NetworkKit';
71
72let want: Want = {
73  deviceId: "",
74  bundleName: "com.example.myvpndemo",
75  abilityName: "MyVpnExtAbility",
76};
77
78@Entry
79@Component
80struct Index {
81  @State message: string = 'Hello World';
82
83  build() {
84    Row() {
85      Column() {
86        Text(this.message)
87          .fontSize(50)
88          .fontWeight(FontWeight.Bold).onClick(() => {
89          console.info("btn click") })
90        Button('Start Extension').onClick(() => {
91          vpnExtension.startVpnExtensionAbility(want);
92        }).width('70%').fontSize(45).margin(16)
93        }.width('100%')
94    }.height('100%')
95  }
96}
97```
98
99如果您的VPN应用未获取用户信任,系统将弹出VPN连接的授权对话框,当获取用户授权后,系统将自动调用并启动您实现的VPN Extension Ability的[onCreate](../reference/apis-network-kit/js-apis-VpnExtensionAbility.md#vpnextensionabilityoncreate)方法将被调用。
100
101目前系统仅支持启动一个VPN连接服务,当VPN已经启动时应用新调用启动接口会收到系统拒绝错误,此时建议您的应用可以提醒用户先断开当前已经激活的VPN应用连接。
102
103
104
105### 停止VPN Extension Ability
106
107当VPN应用需要停止VPN连接时,需要调用stopVpnExtensionAbility接口,携带需要停止的VpnExtensionAbility信息。系统会对调用方做权限校验,stopVpnExtensionAbility的调用方应用必须获取了用户的VPN信任授权,且只允许停止应用自己启动的VpnExtensionAbility,所以接口传入的参数中bundleName需要与您的VPN应用bundleName一致,abilityName为指定停止VPN的VpnExtensionAbility名。
108
109您可参考如下示例:
110
111```ts
112import { common, Want } from '@kit.AbilityKit';
113import { vpnExtension } from '@kit.NetworkKit';
114
115let want: Want = {
116  deviceId: "",
117  bundleName: "com.example.myvpndemo",
118  abilityName: "MyVpnExtAbility",
119};
120
121@Entry
122@Component
123struct Index {
124  @State message: string = 'Hello World';
125
126  build() {
127    Row() {
128      Column() {
129        Text(this.message)
130          .fontSize(50)
131          .fontWeight(FontWeight.Bold).onClick(() => {
132          console.info("btn click") })
133        Button('Start Extension').onClick(() => {
134          vpnExtension.startVpnExtensionAbility(want);
135        }).width('70%').fontSize(45).margin(16)
136        Button('Stop Extension').onClick(() => {
137          console.info("btn end")
138          vpnExtension.stopVpnExtensionAbility(want);
139        }).width('70%').fontSize(45).margin(16)
140
141        }.width('100%')
142    }.height('100%')
143  }
144}
145```
146
147stopVpnExtensionAbility后,您的VPN Extension Ability的[onDestroy](../reference/apis-network-kit/js-apis-VpnExtensionAbility.md#vpnextensionabilityondestroy)方法将被调用,您可在此时destroy vpn连接。
148
149```ts
150import { vpnExtension, VpnExtensionAbility } from '@kit.NetworkKit';
151import { common, Want } from '@kit.AbilityKit';
152import { BusinessError } from '@kit.BasicServicesKit';
153
154let context: vpnExtension.VpnExtensionContext;
155export default class MyVpnExtAbility extends VpnExtensionAbility {
156  onDestroy() {
157    let VpnConnection : vpnExtension.VpnConnection = vpnExtension.createVpnConnection(context);
158    console.info("vpn createVpnConnection: " + JSON.stringify(VpnConnection));
159    VpnConnection.destroy().then(() => {
160      console.info("destroy success.");
161    }).catch((error : BusinessError) => {
162      console.error("destroy fail" + JSON.stringify(error));
163    });
164  }
165}
166```
167
168### 生成VPN Id
169
170创建新的VPN时,应生成一个VPN Id作为VPN的唯一标识。
171可参考如下示例:
172
173```ts
174import VpnExtensionAbility from "@ohos.app.ability.VpnExtensionAbility";
175import { vpnExtension } from "@kit.NetworkKit";
176
177export default class VpnTest extends VpnExtensionAbility {
178  vpnId:string = ''
179
180  getVpnId(){
181    let vpnConnection = vpnExtension.createVpnConnection(this.context);
182    vpnConnection?.generateVpnId().then((data)=>{
183      if (data) {
184        this.vpnId = data;
185      }
186    });
187  }
188}
189```
190
191### 断开VPN
192
193若需断开VPN,可参考如下示例:
194```ts
195import VpnExtensionAbility from "@ohos.app.ability.VpnExtensionAbility";
196import { vpnExtension } from "@kit.NetworkKit";
197
198export default class VpnTest extends VpnExtensionAbility {
199  vpnId: string = 'test_vpn_id'
200  vpnConnection: vpnExtension.VpnConnection | undefined
201
202  destroy(){
203    this.vpnConnection = vpnExtension.createVpnConnection(this.context);
204    this.vpnConnection?.destroy(this.vpnId);
205  }
206}
207```
208
209## 服务生命周期
210
211为了保障设备的网络连接,当系统观察到VPN相关应用出现异常时会主动停止VPN连接:
212
213- 当调用startVpnExtensionAbility接口的应用进程退出时。
214- 当VPN服务进程销毁时。
215
216## VPN Config参数说明
217
218| 名称                | 类型                                                         | 必填 | 说明                                                         |
219| ------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
220| addresses           | Array\<[LinkAddress](../reference/apis-network-kit/js-apis-net-connection.md#linkaddress)\> | 是   | VPN虚拟网卡的IP地址。                                        |
221| routes              | Array\<[RouteInfo](../reference/apis-network-kit/js-apis-net-connection.md#routeinfo)\> | 否   | VPN虚拟网卡的路由信息(目前最多可配置1024条路由)。            |
222| dnsAddresses        | Array\<string\>                                              | 否   | DNS服务器地址信息。配置DNS服务器地址后,VPN启动状态下,被代理的应用上网时,使用配置的DNS服务器进行DNS查询。 |
223| searchDomains       | Array\<string\>                                              | 否   | DNS的搜索域列表。                                            |
224| mtu                 | number                                                       | 否   | 最大传输单元MTU值(单位:字节)。                               |
225| isIPv4Accepted      | boolean                                                      | 否   | 是否支持IPV4,默认值为true。true:支持IPV4;false:不支持IPV4。                                 |
226| isIPv6Accepted      | boolean                                                      | 否   | 是否支持IPV6,默认值为false。true:支持IPV6;false:不支持IPV6。                                |
227| isInternal          | boolean                                                      | 否   | 是否支持内置VPN,默认值为false。true:支持内置VPN;false:不支持内置VPN。                             |
228| isBlocking          | boolean                                                      | 否   | 是否阻塞模式,默认值为false。true:是阻塞模式;false:不是阻塞模式。                                |
229| trustedApplications | Array\<string\>                                              | 否   | 受信任的应用信息列表,以string类型表示的包名。配置此列表后,仅列表中的应用数据才能根据routes被VPN代理。<br>注:trustedApplications和blockedApplications列表不能同时配置。              |
230| blockedApplications | Array\<string\>                                              | 否   | 被阻止的应用信息列表,string类型表示的包名。当配置该列表后,该列表中的应用数据不会被VPN代理,其他应用可以根据routes配置被VPN代理。<br>注:trustedApplications和blockedApplications列表不能同时配置。          |
231
232**示例:**
233
234```ts
235import { vpnExtension} from '@kit.NetworkKit';
236
237let vpnConfig: vpnExtension.VpnConfig = {
238  // 配置VPN虚拟网卡的IP地址。
239  addresses: [{
240    address: {
241      address:'192.x.x.5',
242      family:1
243    },
244    prefixLength:24
245  }],
246  // 配置路由参数。
247  routes: [{
248    // VPN虚拟网卡接口名固定为“vpn-tun”。
249    interface: 'vpn-tun',
250    destination: {
251      address: {
252        address:'10.x.x.8',
253        family:1,
254        port:8080
255      },
256      prefixLength:24
257    },
258    gateway: {
259      address:'10.x.x.5',
260      family:1,
261      port:8080
262    },
263    hasGateway: false,
264    isDefaultRoute: false,
265  }],
266  // 配置最大传输单元值。
267  mtu: 1400,
268  // 配置VPN使用的DNS服务器。
269  dnsAddresses: ['223.x.x.5', '223.x.x.6'],
270  // VPN生效白名单的应用。
271  trustedApplications: ['com.test.browser'],
272  // 不生效VPN黑名单的应用。
273  blockedApplications: ['com.test.games'],
274}
275let context: vpnExtension.VpnExtensionContext;
276
277function vpnCreate(){
278  let VpnConnection: vpnExtension.VpnConnection = vpnExtension.createVpnConnection(context);
279  VpnConnection.create(vpnConfig).then((data) => {
280    console.info("vpn create " + JSON.stringify(data));
281  })
282}
283```
284
285
286
287## VPN Demo示例
288
289OpenHarmony开源项目包含一个名为[VPN](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case)的示例应用。此应用展示了如何设置和连接 VPN 服务。
290
291
292
293