因为有需求 所以有了这次尝试
由于种种原因(手机丢失… 卧室搬迁)
我的 Ubuntu服务器移动了位置 ,而位于原址的网络处于停用状态。
恢复使用的一大方案就是放置一台终端连接那边的网络,然后设置为DMZ主机。
但是很显然,我现在不具备这个条件。
所幸 2020年认识的 HLL(货拉拉)同学赠送了我一台 海信芯片(Hi3798MV310
)的 UNT402H
此前尝试过获取ROOT
权限,刷入 Linux 或者启用一些终端之类的操作,均失败
今天我想到了绝佳方案
编译为 arm
平台 的现成的 frps
完全有条件可以部署在这台设备上,然后我其他没有公网端口使用的设备向这里穿透就可以使用了。
需要考虑的问题
- 停电的可能
- 动态公网IP会重置
- 无法直接操作此终端
解决方案
大概折腾了五个小时
先尝试 adb
连接,测试了frps
确实可行
然后搞这个环境用了许久,Android Studio
我并不熟悉,算是踩了一点坑。
这些折腾完大概花了三小时
实际开发代码
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| package com.curesky.netserver
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import java.io.File import java.io.FileOutputStream import java.io.IOException import com.curesky.netserver.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private val frpsBinName = "frps" private val frpsConfigName = "frps.ini"
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)
initFrpsFiles()
runFrps()
listPrivateFiles() }
private fun initFrpsFiles() { try { val binDir = File(filesDir, "bin").apply { if (!exists()) mkdirs() } copyAssetToFile(frpsBinName, File(binDir, frpsBinName).apply { setExecutable(true) }) copyAssetToFile(frpsConfigName, File(filesDir, frpsConfigName)) } catch (e: Exception) { binding.textView.text = "初始化FRPS失败: ${e.message}" } }
private fun copyAssetToFile(assetName: String, targetFile: File) { try { assets.open(assetName).use { input -> FileOutputStream(targetFile).use { output -> input.copyTo(output) } } } catch (e: IOException) { throw IOException("复制 $assetName 失败", e) } }
private fun runFrps() { val frpsPath = File(filesDir, "bin/$frpsBinName").absolutePath val configPath = File(filesDir, frpsConfigName).absolutePath try {
Runtime.getRuntime().exec("$frpsPath -c $configPath") } catch (e: Exception) { binding.textView.append("\n\n❌ FRPS启动失败: ${e.message}") } }
private fun listPrivateFiles() { val sb = StringBuilder("📁 私有目录文件列表:\n\n")
listFilesInDirectory(filesDir, sb) binding.textView.text = sb.toString() }
private fun listFilesInDirectory(dir: File, sb: StringBuilder, indent: String = "") { dir.listFiles()?.forEach { file -> val prefix = if (file.isDirectory) "📂 " else "📄 " sb.append("$indent$prefix${file.name}\n") if (file.isDirectory) { listFilesInDirectory(file, sb, "$indent ") } } } }
|
BootReceiver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.curesky.netserver;
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent;
public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { Intent launchIntent = new Intent(context, MainActivity.class); launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(launchIntent);
} } }
|
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.curesky.netserver">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.NetServer">
<activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.USER_PRESENT" /> </intent-filter> </receiver>
</application> </manifest>
|
以上三个内容基本是我开发的重点所在
两个核心逻辑实现,和一个权限实现
想总结些什么,但是身体还没调理好,所以仅能记录这些了。