Android VpnService(JSch SSH连接)能连服务器但无法访问互联网
问题原因与解决方案
你的代码虽成功建立SSH连接与VPN虚拟接口,但未实现VPN接口与SSH代理间的流量转发逻辑,这是无法访问互联网的核心原因。此外代码存在几处关键配置错误,具体问题与修复步骤如下:
1. 代码中的核心错误
(1)重复调用establish()导致配置无效
构建VPN时连续调用两次builder.establish(),且allowFamily()在首次establish()后添加,这部分配置完全不生效:
// 错误:首次调用establish()后,builder配置已固化,后续修改无效 builder.setSession("MyVpnService") .addAddress("10.0.0.1", 24) .addRoute("0.0.0.0", 0) .addDnsServer("8.8.8.8") .setBlocking(true) .establish(); // 首次调用,配置已生效 vpnInterface = builder.establish(); // 重复调用无意义 builder.allowFamily(OsConstants.AF_INET); // 此代码在establish后执行,完全无效
(2)缺失VPN接口流量的读写转发逻辑
Android的VpnService仅创建虚拟网卡、将系统流量导向该接口,但你必须自行编写代码读取vpnInterface内的流量,通过SSH的SOCKS5代理转发至VPS,再将VPS返回的流量写回vpnInterface。你的代码完全缺少这部分核心逻辑,导致流量到VPN接口后直接中断。
(3)SSH端口转发配置需验证
你设置的端口转发sshSession.setPortForwardingL(0, "localhost", 1080),表示将本地随机端口转发到VPS的localhost:1080,需确保VPS的1080端口确实运行着SOCKS5服务(比如通过sshd开启或专用SOCKS5代理)。
2. 修复后的完整代码示例
(1)修改MyVpnService类
核心是添加流量转发线程,处理VPN接口的读写,并通过SOCKS5代理转发流量:
package com.example.myapplication; import android.content.Intent; import android.net.VpnService; import android.os.ParcelFileDescriptor; import android.system.OsConstants; import android.util.Log; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.Socket; public class MyVpnService extends VpnService { private ParcelFileDescriptor vpnInterface; private Session sshSession; private Thread trafficForwardThread; private int localSocksPort; @Override public void onCreate() { super.onCreate(); JSch jsch = new JSch(); try { // 替换为你的VPS信息 sshSession = jsch.getSession("SERVER_USERNAME", "SERVER_IP", SERVER_PORT); sshSession.setPassword("SERVER_PASSWORD"); sshSession.setConfig("StrictHostKeyChecking", "no"); } catch (JSchException e) { Log.e("MyVpnService", "SSH初始化失败", e); throw new RuntimeException(e); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { Thread vpnThread = new Thread(() -> { try { // 建立SSH连接 sshSession.connect(); // 将本地随机端口转发到VPS的SOCKS5端口(假设VPS的SOCKS5运行在1080) localSocksPort = sshSession.setPortForwardingL(0, "localhost", 1080); Log.d("MyVpnService", "本地SOCKS5端口:" + localSocksPort); // 正确配置VPN接口,仅调用一次establish() VpnService.Builder builder = new VpnService.Builder(); vpnInterface = builder.setSession("MyVpnService") .addAddress("10.0.0.2", 24) // 客户端地址,避免与VPS内网冲突 .addRoute("0.0.0.0", 0) // 路由所有IPv4流量 .addDnsServer("8.8.8.8") .allowFamily(OsConstants.AF_INET) .setBlocking(true) .establish(); // 启动流量转发线程 startTrafficForwarding(); } catch (Exception e) { Log.e("MyVpnService", "VPN启动失败", e); cleanup(); } }); vpnThread.start(); return START_STICKY; } // 核心:处理VPN接口流量转发到SOCKS5代理 private void startTrafficForwarding() { trafficForwardThread = new Thread(() -> { FileInputStream in = new FileInputStream(vpnInterface.getFileDescriptor()); FileOutputStream out = new FileOutputStream(vpnInterface.getFileDescriptor()); try { byte[] buffer = new byte[1500]; while (!Thread.interrupted()) { // 读取VPN接口的流量 int length = in.read(buffer); if (length > 0) { // 连接到本地SOCKS5代理端口 try (Socket socksSocket = new Socket("localhost", localSocksPort)) { // 将流量发送到SOCKS5代理 socksSocket.getOutputStream().write(buffer, 0, length); // 读取代理返回的流量,写回VPN接口 int responseLength = socksSocket.getInputStream().read(buffer); if (responseLength > 0) { out.write(buffer, 0, responseLength); out.flush(); } } catch (IOException e) { Log.e("MyVpnService", "流量转发失败", e); } } } } catch (IOException e) { Log.e("MyVpnService", "流量读写失败", e); } finally { try { in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } } }); trafficForwardThread.start(); } private void cleanup() { if (trafficForwardThread != null) { trafficForwardThread.interrupt(); } if (vpnInterface != null) { try { vpnInterface.close(); } catch (IOException e) { e.printStackTrace(); } } if (sshSession != null && sshSession.isConnected()) { sshSession.disconnect(); } } @Override public void onDestroy() { cleanup(); super.onDestroy(); } }
(2)额外注意事项
- 确保VPS已开启SOCKS5代理:可在
/etc/ssh/sshd_config中添加GatewayPorts yes与AllowTcpForwarding yes,重启sshd服务后,通过ssh -D 1080 user@vps-ip验证SOCKS5可用性。 - Android 12及以上版本需
POST_NOTIFICATIONS权限,需在Manifest中添加并动态申请,否则前台服务无法正常运行。 - 上述流量转发逻辑为基础实现,可进一步优化(如使用NIO或连接池)。
内容的提问来源于stack exchange,提问作者soheyl momeni
相关产品推荐
相关产品推荐

