排查二次启动的问题

This commit is contained in:
asxalex 2025-07-18 06:25:27 +08:00
parent 867fb5ca72
commit 87bee05140
15 changed files with 497 additions and 174 deletions

Binary file not shown.

View File

@ -1,5 +1,6 @@
package com.jihe.punchnet
import android.app.Application
import android.content.Context
import android.content.Intent
import android.net.VpnService
@ -31,12 +32,21 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.compose.rememberNavController
import com.jihe.punchnet.data.ButtonState
import com.jihe.punchnet.data.ButtonViewModel
import com.jihe.punchnet.data.RouteViewModel
import com.jihe.punchnet.data.ServiceViewModel
import com.jihe.punchnet.screen.MainApp
import com.jihe.punchnet.ui.theme.PunchnetTheme
import kotlinx.coroutines.flow.onEach
class PunchnetApp: Application() {
val buttonModel: ButtonViewModel by lazy {
ViewModelProvider.AndroidViewModelFactory.getInstance(this)
.create(ButtonViewModel::class.java)
}
}
class MainActivity : ComponentActivity() {
private val TAG = "MainActivity"
@ -58,6 +68,18 @@ class MainActivity : ComponentActivity() {
}
}
/*
val buttonModel: ButtonViewModel by viewModels {
object: ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun<T: ViewModel> create(modelClass: Class<T>): T {
return ButtonViewModel(application) as T
}
}
}
*/
/*
fun prepareAndStartVPN() {
@ -97,9 +119,12 @@ class MainActivity : ComponentActivity() {
Log.d("DIR", "filesdir = ${this.filesDir}")
val buttonModel = ButtonViewModel()
buttonModel.updateState(ButtonState.ButtonStopped)
setContent {
PunchnetTheme {
MainApp(serviceModel, viewModel, rememberNavController())
MainApp(buttonModel, serviceModel, viewModel, rememberNavController())
/*
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
StartStop(

View File

@ -13,6 +13,9 @@ import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.lifecycle.ViewModelProvider
import com.jihe.punchnet.data.ButtonRepository
import com.jihe.punchnet.data.ButtonState
import com.jihe.punchnet.data.ButtonViewModel
import com.jihe.punchnet.data.RouteItem
import com.jihe.punchnet.data.RouteViewModel
import com.jihe.punchnet.sdlan.config.Arguments
@ -42,6 +45,7 @@ import java.io.FileOutputStream
import java.util.concurrent.atomic.AtomicBoolean
class PunchnetService : VpnService() , IfaceTun {
private val TAG = "PunchnetService"
private var scope = CoroutineScope(Dispatchers.IO)
@ -50,9 +54,7 @@ class PunchnetService : VpnService() , IfaceTun {
private var vpnDescriptor: ParcelFileDescriptor? = null
private val serviceLock = Any()
private val isRunning = AtomicBoolean(false)
private val start_stop_channel = Channel<StartStopChanInfo>(100)
// private val isRunning = AtomicBoolean(false)
var input: FileInputStream? = null
var output: FileOutputStream? = null
@ -129,17 +131,24 @@ class PunchnetService : VpnService() , IfaceTun {
}
vpnDescriptor = tempVpnDescriptor
.establish()
input = FileInputStream(vpnDescriptor!!.fileDescriptor)
output = FileOutputStream(vpnDescriptor!!.fileDescriptor)
.establish()?.apply {
input = FileInputStream(fileDescriptor)
output = FileOutputStream(fileDescriptor)
}
ButtonRepository.updateState(ButtonState.ButtonStarted)
}
private fun disconnect() {
synchronized(serviceLock) {
if (ButtonRepository.buttonState.value != ButtonState.ButtonStarted) {
return
}
/*
if (!isRunning.get()) {
return
}
*/
ButtonRepository.updateState(ButtonState.ButtonStopping)
Toast.makeText(this, "stop vpn called", Toast.LENGTH_LONG).show()
input?.close()
@ -149,15 +158,23 @@ class PunchnetService : VpnService() , IfaceTun {
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
isRunning.set(false)
// isRunning.set(false)
}
ButtonRepository.updateState(ButtonState.ButtonStopped)
}
private fun connect(startArg: PunchnetServiceArgument?) {
synchronized(serviceLock) {
if (ButtonRepository.buttonState.value != ButtonState.ButtonStopped) {
return
}
/*
if (isRunning.get()) {
return
}
*/
ButtonRepository.updateState(ButtonState.ButtonStarting)
val iface = this
val server = "punchnet.aioe.tech"
@ -194,11 +211,13 @@ class PunchnetService : VpnService() , IfaceTun {
startForeground(1, notification)
scope.launch {
run_sdlan(scope, start_stop_channel, iface, argument, startArg)
run_sdlan(scope, iface, argument, startArg)
}
isRunning.set(true)
// isRunning.set(true)
}
// ButtonRepository.updateState(ButtonState.ButtonStarted)
// buttonViewModel.changeButtonState(ButtonState.ButtonStarted)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -209,7 +228,6 @@ class PunchnetService : VpnService() , IfaceTun {
disconnect()
START_STICKY
} else {
scope = CoroutineScope(Dispatchers.IO)
arpTable = ARPTable(scope)

View File

@ -0,0 +1,34 @@
package com.jihe.punchnet.data
import android.app.Application
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
object ButtonRepository {
private val _buttonState: MutableStateFlow<ButtonState> = MutableStateFlow(ButtonState.ButtonStopped)
val buttonState: StateFlow<ButtonState> = _buttonState
fun updateState(state: ButtonState) {
_buttonState.value = state
}
}
sealed class ButtonState(val text: String, val enabled: Boolean) {
object ButtonStarted: ButtonState("停止", true)
object ButtonStarting: ButtonState("启动中", false)
object ButtonStopped: ButtonState("启动", true)
object ButtonStopping: ButtonState("停止中", false)
}
class ButtonViewModel(): ViewModel() {
val buttonState = ButtonRepository.buttonState
fun updateState(state: ButtonState) {
ButtonRepository.updateState(state)
}
}

View File

@ -27,20 +27,24 @@ import kotlinx.coroutines.flow.forEach
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
class ServiceViewModel(application: Application): AndroidViewModel(application) {
/*
private val _isRunning = mutableStateOf(false)
val isRunning: State<Boolean> = _isRunning
*/
fun startService(context: Context, routes: Array<RouteItem>) {
fun startService(context: Context, routes: Array<RouteItem>, token: String) {
// should has the permission
startVpnService(context, routes)
_isRunning.value = true
startVpnService(context, routes, token)
// ButtonRepository.updateState(ButtonState.ButtonStarting)
// _isRunning.value = true
}
private fun startVpnService(context: Context, routes: Array<RouteItem>) {
private fun startVpnService(context: Context, routes: Array<RouteItem>, token: String) {
val intent = Intent(context, PunchnetService::class.java)
intent.putExtra("argument", PunchnetServiceArgument(
"",
token,
routes,
))
@ -59,7 +63,7 @@ class ServiceViewModel(application: Application): AndroidViewModel(application)
PunchnetService.ACTION_DISCONNECT })
// stopService(Intent(this, PunchnetService::class.java))
Log.d("STOPPED PUNCHNET", "stopping PUNCHNET")
_isRunning.value = false
// ButtonRepository.updateState(ButtonState.ButtonStopping)
// Toast.makeText(this, "VPN service stopped", Toast.LENGTH_SHORT).show()
}
}

View File

@ -1,8 +1,48 @@
package com.jihe.punchnet.helper
import android.app.Activity
import android.content.Context
import com.jihe.punchnet.data.RouteItem
import com.jihe.punchnet.sdlan.network.maskDigitToInt
const val PreferenceRepositoryName = "pref"
sealed class PreferenceName(val name: String) {
object PreferenceToken: PreferenceName("token")
}
fun getPreferenceString(context: Context, name: PreferenceName): String? {
val preference = context.getSharedPreferences(PreferenceRepositoryName, Activity.MODE_PRIVATE)
try {
return preference.getString(name.name, "")
} catch (e: Exception) {
return null
}
}
fun getPreferenceInt(context: Context, name: PreferenceName): Int? {
val preference = context.getSharedPreferences(PreferenceRepositoryName, Activity.MODE_PRIVATE)
try {
return preference.getInt(name.name, 0)
} catch (e: Exception) {
return null
}
}
fun setPreferenceString(context: Context, name: PreferenceName, value: String) {
val preference = context.getSharedPreferences(PreferenceRepositoryName, Activity.MODE_PRIVATE)
val editor = preference.edit()
editor.putString(name.name, value)
editor.apply()
}
fun setPreferenceInt(context: Context, name: PreferenceName, value: Int) {
val preference = context.getSharedPreferences(PreferenceRepositoryName, Activity.MODE_PRIVATE)
val editor = preference.edit()
editor.putInt(name.name, value)
editor.apply()
}
sealed class Screen(val route: String) {
object ScreenRoutes: Screen("routes")
object ScreenMain: Screen("main")

View File

@ -17,6 +17,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -34,29 +35,34 @@ import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.jihe.punchnet.R
import com.jihe.punchnet.RouteInfo
import com.jihe.punchnet.data.ButtonRepository
import com.jihe.punchnet.data.ButtonState
import com.jihe.punchnet.data.ButtonViewModel
import com.jihe.punchnet.data.RouteViewModel
import com.jihe.punchnet.data.ServiceViewModel
@Composable
fun AppNavHost2(
buttonViewModel: ButtonViewModel,
serviceViewModel: ServiceViewModel,
routeViewModel: RouteViewModel = viewModel(),
navController: NavHostController,
paddingValues: PaddingValues
) {
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route,
modifier = Modifier.padding(paddingValues)
) {
composable(Screen.MainScreen.route) {
HomeScreen(serviceViewModel, routeViewModel)
HomeScreen(buttonViewModel, serviceViewModel, routeViewModel)
}
composable(Screen.RouteScreen.route) {
CustomHeaderScreen(
"路由设置"
) {
RouteScreen(!serviceViewModel.isRunning.value, routeViewModel)
RouteScreen(ButtonRepository.buttonState.collectAsState().value != ButtonState.ButtonStarted, routeViewModel)
// LightScreen()
}
}

View File

@ -1,5 +1,6 @@
package com.jihe.punchnet.screen
import android.app.Activity.MODE_PRIVATE
import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.content.Context
@ -9,8 +10,15 @@ import android.os.Build
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@ -20,13 +28,22 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -38,17 +55,116 @@ import com.jihe.punchnet.PunchnetService
import com.jihe.punchnet.PunchnetServiceArgument
import com.jihe.punchnet.R
import com.jihe.punchnet.RouteInfo
import com.jihe.punchnet.data.ButtonRepository
import com.jihe.punchnet.data.ButtonState
import com.jihe.punchnet.data.ButtonViewModel
import com.jihe.punchnet.data.RouteViewModel
import com.jihe.punchnet.data.ServiceViewModel
import com.jihe.punchnet.helper.PreferenceName
import com.jihe.punchnet.helper.getPreferenceString
import com.jihe.punchnet.helper.setPreferenceString
import kotlin.math.exp
data class HomeDropDownInfo(
@DrawableRes val icon: Int,
val name: String,
val callback: (()->Unit)? = null,
)
@Composable
fun HomeDropdownMenu(
showTokenDialog: MutableState<Boolean>,
) {
var expanded by remember { mutableStateOf(false) }
// var showTokenDialog by remember { mutableStateOf(false) }
// val showSettingDialog by remember { mutableStateOf(false) }
val context = LocalContext.current
val homeItems = arrayOf(
HomeDropDownInfo(
R.drawable.tag,
"修改token",
{
showTokenDialog.value = true
}
),
/*
HomeDropDownInfo(
R.drawable.preferences,
"修改云端配置"
)
*/
)
Box() {
IconButton(
interactionSource = remember { MutableInteractionSource() },
modifier = Modifier.indication(
interactionSource = remember { MutableInteractionSource() },
indication = null,
),
onClick = {
expanded = true
}
) {
Icon(
Icons.Default.Settings,
contentDescription = "settings",
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = {expanded = !expanded}
) {
homeItems.forEach { item ->
DropdownMenuItem(
onClick = {
expanded = false
item.callback?.invoke()
},
text = {
Row (
verticalAlignment = Alignment.CenterVertically
){
Icon(
painter = painterResource(item.icon),
modifier = Modifier.size(24.dp),
contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground
)
Spacer(
modifier = Modifier.padding(start = 8.dp)
)
Text(text=item.name)
}
}
)
}
}
}
}
@Composable
fun HomeScreen(
buttonViewModel: ButtonViewModel,
serviceViewModel: ServiceViewModel,
routeViewModel: RouteViewModel,
// started: MutableState<Boolean>,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val tkPref = getPreferenceString(context, PreferenceName.PreferenceToken)
var tokenvalue by remember {mutableStateOf(tkPref?:"")}
val allroutes = routeViewModel.allRoutes.collectAsState(emptyList())
val vpnLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@ -57,7 +173,9 @@ fun HomeScreen(
RESULT_OK -> {
// serviceViewModel.startService(context)
// serviceViewModel.setVPNPermission(true)
serviceViewModel.startService(context, allroutes.value.toTypedArray())
val tk = getPreferenceString(context, PreferenceName.PreferenceToken)?:""
println("now token value2 = '${tokenvalue}'")
serviceViewModel.startService(context, allroutes.value.toTypedArray(), tk)
}
RESULT_CANCELED -> {
Toast.makeText(context, "vpn permission denied", Toast.LENGTH_SHORT).show()
@ -65,27 +183,54 @@ fun HomeScreen(
}
}
val buttonState = buttonViewModel.buttonState.collectAsState()
val showTokenDialog = remember { mutableStateOf(false) }
CustomHeaderScreen(
"",
onBack = null,
onMenu = {
Icon(
Icons.Default.Settings,
contentDescription = "settings",
modifier = Modifier.fillMaxHeight()
)
// NavListMenu(navController, dbdao)
/*
navController.navigate(
Screen.ActionDetail.route.replace(
"{action_name}",
""
)
)
*/
HomeDropdownMenu(showTokenDialog)
}
) {
if (showTokenDialog.value) {
AlertDialog(
onDismissRequest = {showTokenDialog.value=false},
confirmButton = {
Button(
onClick = {
setPreferenceString(context, PreferenceName.PreferenceToken, tokenvalue)
println("now token value = '${tokenvalue}'")
showTokenDialog.value = false
}
) {
Text("确定")
}
},
title = {
Text("修改token")
},
text = {
TextField(
value = tokenvalue,
onValueChange = { newValue ->
tokenvalue = newValue.trim()
},
leadingIcon = {
Icon(
painter = painterResource(R.drawable.tag),
contentDescription = "token",
modifier = Modifier.size(24.dp)
)
},
)
}
)
}
Column(
modifier = Modifier.fillMaxWidth()
.padding(top=50.dp),
@ -114,30 +259,38 @@ fun HomeScreen(
Button(
onClick = {
if (serviceViewModel.isRunning.value) {
if (buttonViewModel.buttonState.value == ButtonState.ButtonStarted) {
// if (serviceViewModel.isRunning.value) {
// if is running, should stop service
serviceViewModel.stopVpnService(context)
} else {
} else if (buttonViewModel.buttonState.value == ButtonState.ButtonStopped){
val intent = VpnService.prepare(context)
if (intent != null) {
vpnLauncher.launch(intent)
} else {
serviceViewModel.startService(context, allroutes.value.toTypedArray())
val tk = getPreferenceString(context, PreferenceName.PreferenceToken)?:""
println("now token value1 = '${tokenvalue}'")
serviceViewModel.startService(context, allroutes.value.toTypedArray(), tk)
}
}
// started.value = !started.value
},
enabled = buttonState.value.enabled,
shape = RoundedCornerShape(10.dp),
modifier = Modifier.padding(top=48.dp)
.width(120.dp)
.height(40.dp)
) {
Text(
buttonState.value.text
// buttonViewModel.buttonText.value
/*
if (serviceViewModel.isRunning.value) {
"停止"
} else {
"启动"
}
*/
)
}
}

View File

@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.jihe.punchnet.data.ButtonViewModel
import com.jihe.punchnet.data.RouteViewModel
import com.jihe.punchnet.data.ServiceViewModel
@ -37,6 +38,7 @@ val bottomNavItems = listOf(
@Composable
fun MainApp(
buttonViewModel: ButtonViewModel = viewModel(),
serviceViewModel: ServiceViewModel = viewModel(),
routeModel: RouteViewModel = viewModel(),
navController: NavHostController,
@ -54,7 +56,7 @@ fun MainApp(
modifier = Modifier.padding(paddingValues).size(200.dp)
)
*/
AppNavHost2(serviceViewModel, routeModel, navController, paddingValues)
AppNavHost2(buttonViewModel, serviceViewModel, routeModel, navController, paddingValues)
}
}

View File

@ -2,6 +2,8 @@ package com.jihe.punchnet.sdlan.network
import android.util.Log
import com.jihe.punchnet.PunchnetServiceArgument
import com.jihe.punchnet.data.ButtonRepository
import com.jihe.punchnet.data.ButtonState
import com.jihe.punchnet.protobuf.PunchProto.SDLDevAddr
import com.jihe.punchnet.protobuf.PunchProto.SDLRegisterSuper
import com.jihe.punchnet.protobuf.PunchProto.SDLRegisterSuperAck
@ -18,9 +20,12 @@ import com.jihe.punchnet.sdlan.utils.UniqueNodeID
import com.jihe.punchnet.sdlan.utils.ipToString
import com.jihe.punchnet.sdlan.utils.macToString
import com.jihe.punchnet.sdlan.utils.parseScoketAddressV4FromString
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
@ -91,9 +96,11 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
when(nakcode) {
NakMsgCode.InvalidToken, NakMsgCode.NodeDisabled-> {
node.startStopChannel.send(StartStopChanInfo(StartStopFlag.IsStop, null))
exitProcess(-1)
ButtonRepository.updateState(ButtonState.ButtonStopped)
scope.cancel()
}
else -> {
ButtonRepository.updateState(ButtonState.ButtonStopped)
node.startStopChannel.send(StartStopChanInfo(StartStopFlag.IsStop, null))
}
}
@ -131,10 +138,9 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
println("error packet type: ${data.packetType.toUByte()}")
}
}
}
suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartStopChanInfo>, iface: Iface, argument: Arguments, routeinfo: PunchnetServiceArgument?) {
suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments, routeinfo: PunchnetServiceArgument?) {
UniqueNodeID.setBaseDir(argument.baseDir)
val edgeUUID = UniqueNodeID.getUUID()
val config = parseConfig(edgeUUID, argument)
@ -147,7 +153,7 @@ suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartSt
val toSocket = Channel<ByteArray>(100)
// val start_stop_channel = Channel<StartStopChanInfo>(100)
val start_stop_channel = Channel<StartStopChanInfo>(100)
initEdge(scope, iface, argument.token, config, toSocket, start_stop_channel)
val tcp = argument.tcp.split(":")
@ -156,7 +162,6 @@ suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartSt
val node = Node.getInstance()
val before = ubyteArrayOf(126u,162u,25u,63u,148u,147u,198u,41u,69u,165u,149u,101u,153u,82u,190u,21u,48u,120u,26u,64u,142u,103u,159u,60u,47u,129u,176u,17u,232u,210u,36u,56u)
val encrypted = node.rsa.encrypt(before.toByteArray(), use_private_key = false)
// println("encrypted: ${encrypted.contentToString()}")
@ -227,6 +232,8 @@ suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartSt
} catch (e: Exception) {
println("iface read is cancelled")
node.iface?.close()
node.udpSockV4.close()
node.udpSockMulticast?.close()
}
}
/*
@ -253,7 +260,6 @@ suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartSt
// CoroutineScope(Dispatchers.IO).async {
scope.launch {
try {
initTCPConn(
scope,
tcp[0], tcp[1].toInt(),
@ -268,13 +274,12 @@ suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartSt
},
null,
)
} finally {
println("initTCPConn is cancelled")
}
}
println("sending start")
start_stop_channel.send(StartStopChanInfo(StartStopFlag.IsStart, null))
println("sent start")
val cancel = Channel<Boolean>(100)
runEdgeLoop(scope, node, cancel)
@ -295,16 +300,23 @@ suspend fun runEdgeLoop(scope: CoroutineScope, node: Node, cancel: ReceiveChanne
suspend fun loopSocketV4(scope: CoroutineScope, node: Node, sock: SDLanSocket, cancel: ReceiveChannel<Boolean>) {
val job_stun_request = scope.async {
try {
while(true) {
delay(10_000)
node.sendStunRequest()
}
} catch (e: Exception) {
}
}
val job_handle_packet = scope.async {
try {
while(true) {
readAndParsePacket(scope, node, sock)
}
} catch (e: Exception) {
sock.close()
}
}
select<Unit> {
@ -435,9 +447,11 @@ suspend fun initTCPConn(
onDisconnected: suspend ()->Unit,
connectingChan: SendChannel<ConnectingState>?
) {
val started = AtomicBoolean(false)
// var started: Boolean = false
var startPktID: Int? = null
val node = Node.getInstance()
while (true) {
connectingChan?.send(ConnectingState.NotConnected)
@ -467,12 +481,18 @@ suspend fun initTCPConn(
TerminalLogger.debugf { "connecting to $tcpHost:$tcpPort" }
socket = Socket(tcpHost, tcpPort)
}
}catch(e: CancellationException) {
withContext(NonCancellable) {
node.iface?.close()
}
return
} catch (e: Exception) {
TerminalLogger.errorf { "failed to connect to ${tcpHost}:${tcpPort}: $e" }
delay(3000)
continue
}
try {
val node = Node.getInstance()
val outIP = ByteBuffer.wrap(socket.localAddress.address).getInt()
node.outerIPV4.set(outIP)
@ -491,8 +511,8 @@ suspend fun initTCPConn(
onMessage(scope, tcpPacket)
}
} finally {
TerminalLogger.errorf {"input closing"}
input.close()
TerminalLogger.errorf { "input closing" }
}
}
@ -541,7 +561,7 @@ suspend fun initTCPConn(
break
}
} catch (e: Exception) {
started.set(false)
// started.set(false)
break
}
@ -579,6 +599,12 @@ suspend fun initTCPConn(
println("m5")
delay(1000)
} catch (e: Exception) {
withContext(NonCancellable) {
node.iface?.close()
socket.close()
}
}
}
}

View File

@ -23,8 +23,6 @@ class SDLanSocket(val scope: CoroutineScope, val addr: String, val port: Int, va
sock
}
private var job: Job? = null
fun loop(): ReceiveChannel<DatagramPacket> {
val channel = Channel<DatagramPacket>(100)
scope.launch {
@ -44,7 +42,7 @@ class SDLanSocket(val scope: CoroutineScope, val addr: String, val port: Int, va
connection.send(packet)
}
} catch(e: Exception) {
TerminalLogger.errorf {"Failed to send to: $e"}
TerminalLogger.errorf {"Failed to send to: $e, localport: ${connection.localPort}"}
}
}
@ -59,7 +57,6 @@ class SDLanSocket(val scope: CoroutineScope, val addr: String, val port: Int, va
}
suspend fun close() {
job?.cancelAndJoin()
connection.close()
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M802.1,102.4L222.2,102.4C156.3,102.4 102.4,156 102.4,221.9v579.9A120.1,120.1 0,0 0,222.2 921.6L802.1,921.6c65.9,0 119.5,-53.9 119.5,-119.8L921.6,221.9C921.6,156 868,102.4 802.1,102.4zM751.3,767.7h-297v11.6c0,19.1 -15.4,34.1 -34.1,34.1s-34.1,-15 -34.1,-34.1v-11.6h-112.6c-19.1,0 -34.1,-15.4 -34.1,-34.1s15,-34.1 34.1,-34.1h112.6v-11.6c0,-18.8 15.4,-34.1 34.1,-34.1s34.1,15.4 34.1,34.1v11.6h297c18.8,0 34.1,15.4 34.1,34.1s-15.4,34.1 -34.1,34.1zM751.3,543.4h-123.9v11.6c0,18.8 -15.4,34.1 -34.1,34.1s-34.1,-15.4 -34.1,-34.1v-11.6L273.4,543.4c-19.1,0 -34.1,-15.4 -34.1,-34.1 0,-19.1 15,-34.1 34.1,-34.1h285.7v-11.9c0,-18.8 15.4,-34.1 34.1,-34.1s34.1,15.4 34.1,34.1v11.9h123.9c18.8,0 34.1,15 34.1,34.1 0,18.8 -15.4,34.1 -34.1,34.1zM751.3,323.9L405.2,323.9v6.8c0,18.8 -15.4,34.1 -34.1,34.1s-34.1,-15.4 -34.1,-34.1v-6.8L273.4,323.9c-19.1,0 -34.1,-15.4 -34.1,-34.1s15,-34.1 34.1,-34.1h63.5L336.9,238.9c0,-18.8 15.4,-34.1 34.1,-34.1s34.1,15.4 34.1,34.1v16.7h346.1c18.8,0 34.1,15.4 34.1,34.1s-15.4,34.1 -34.1,34.1z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M818.2,268.3c0.2,-34.9 -28.7,-64.3 -63.7,-64.6 -34.2,-0.3 -64.3,29.5 -64.3,64 0,34.3 28.9,63.6 63.3,64C788.2,332.1 818,302.9 818.2,268.3M128,640 L128,595.1c0,-0.4 1,-0.8 1,-1.1C132.3,570.2 144.4,551.5 161.2,534.8c125.6,-125.3 251,-250.8 376.5,-376.2C542.1,154.2 546.8,150 551.7,146.2 564,136.3 578.8,131.8 593.7,128l220.8,0c0.6,0.4 1.2,1 1.8,1.2 43.5,6.2 80.3,46.9 79.6,96.1 -0.8,63.2 -0.3,126.6 -0.1,189.9 0.1,26.6 -9.3,49.2 -28,67.9 -127.9,128 -255.7,256.1 -383.8,383.8 -39.2,39.1 -97.5,38.8 -136.8,-0.1 -63.6,-63.2 -126.7,-126.7 -190.1,-190 -11.9,-11.9 -20.6,-25.5 -25.3,-41.9C130.4,630.1 128,625.3 128,620.6"
android:fillColor="#2c2c2c"/>
</vector>