mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
wip
This commit is contained in:
@@ -18,18 +18,58 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.google.android.mobly.snippet.Snippet;
|
||||
import com.google.android.mobly.snippet.rpc.Rpc;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
class Runner {
|
||||
public UUID mId;
|
||||
private final Mode mMode;
|
||||
private final String mModeName;
|
||||
private final String mScenario;
|
||||
private final AppViewModel mModel;
|
||||
|
||||
Runner(Mode mode, String modeName, String scenario, AppViewModel model) {
|
||||
this.mId = UUID.randomUUID();
|
||||
this.mMode = mode;
|
||||
this.mModeName = modeName;
|
||||
this.mScenario = scenario;
|
||||
this.mModel = model;
|
||||
}
|
||||
|
||||
public JSONObject toJson() throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("id", mId.toString());
|
||||
result.put("mode", mModeName);
|
||||
result.put("scenario", mScenario);
|
||||
result.put("model", AutomationSnippet.modelToJson(mModel));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mModel.abort();
|
||||
}
|
||||
|
||||
public void waitForCompletion() {
|
||||
mMode.waitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
public class AutomationSnippet implements Snippet {
|
||||
private static final String TAG = "btbench.snippet";
|
||||
private final BluetoothAdapter mBluetoothAdapter;
|
||||
private final Context mContext;
|
||||
private final ArrayList<Runner> mRunners = new ArrayList<>();
|
||||
|
||||
public AutomationSnippet() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
@@ -40,30 +80,41 @@ public class AutomationSnippet implements Snippet {
|
||||
}
|
||||
}
|
||||
|
||||
private void runScenario(AppViewModel model, String mode, String scenario) {
|
||||
Mode runner;
|
||||
private Runner runScenario(AppViewModel model, String mode, String scenario) {
|
||||
Mode runnable;
|
||||
switch (mode) {
|
||||
case "rfcomm-client":
|
||||
runner = new RfcommClient(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||
runnable = new RfcommClient(model, mBluetoothAdapter,
|
||||
(PacketIO packetIO) -> createIoClient(model, scenario,
|
||||
packetIO));
|
||||
break;
|
||||
|
||||
case "rfcomm-server":
|
||||
runner = new RfcommServer(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||
runnable = new RfcommServer(model, mBluetoothAdapter,
|
||||
(PacketIO packetIO) -> createIoClient(model, scenario,
|
||||
packetIO));
|
||||
break;
|
||||
|
||||
case "l2cap-client":
|
||||
runner = new L2capClient(model, mBluetoothAdapter, mContext, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||
runnable = new L2capClient(model, mBluetoothAdapter, mContext,
|
||||
(PacketIO packetIO) -> createIoClient(model, scenario,
|
||||
packetIO));
|
||||
break;
|
||||
|
||||
case "l2cap-server":
|
||||
runner = new L2capServer(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||
runnable = new L2capServer(model, mBluetoothAdapter,
|
||||
(PacketIO packetIO) -> createIoClient(model, scenario,
|
||||
packetIO));
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
runner.run(true);
|
||||
runnable.run();
|
||||
Runner runner = new Runner(runnable, mode, scenario, model);
|
||||
mRunners.add(runner);
|
||||
return runner;
|
||||
}
|
||||
|
||||
private IoClient createIoClient(AppViewModel model, String scenario, PacketIO packetIO) {
|
||||
@@ -85,35 +136,83 @@ public class AutomationSnippet implements Snippet {
|
||||
}
|
||||
}
|
||||
|
||||
private static JSONObject resultFromModel(AppViewModel model) throws JSONException {
|
||||
public static JSONObject modelToJson(AppViewModel model) throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
JSONObject stats = new JSONObject();
|
||||
result.put("stats", stats);
|
||||
JSONObject throughputStats = new JSONObject();
|
||||
stats.put("throughput", throughputStats);
|
||||
throughputStats.put("average", model.getThroughput());
|
||||
JSONObject rttStats = new JSONObject();
|
||||
stats.put("rtt", rttStats);
|
||||
rttStats.put("compound", model.getStats());
|
||||
result.put("status", model.getStatus());
|
||||
result.put("running", model.getRunning());
|
||||
result.put("l2cap_psm", model.getL2capPsm());
|
||||
if (model.getStatus().equals("OK")) {
|
||||
JSONObject stats = new JSONObject();
|
||||
result.put("stats", stats);
|
||||
stats.put("throughput", model.getThroughput());
|
||||
JSONObject rttStats = new JSONObject();
|
||||
stats.put("rtt", rttStats);
|
||||
rttStats.put("compound", model.getStats());
|
||||
} else {
|
||||
result.put("last_error", model.getLastError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Runner findRunner(String runnerId) {
|
||||
for (Runner runner : mRunners) {
|
||||
if (runner.mId.toString().equals(runnerId)) {
|
||||
return runner;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Rpc(description = "Run a scenario in RFComm Client mode")
|
||||
public JSONObject runRfcommClient(String scenario, String peerBluetoothAddress, int packetCount, int packetSize, int packetInterval) throws JSONException {
|
||||
public JSONObject runRfcommClient(String scenario, String peerBluetoothAddress, int packetCount,
|
||||
int packetSize, int packetInterval) throws JSONException {
|
||||
assert (mBluetoothAdapter != null);
|
||||
|
||||
// We only support "send" and "ping" for this mode for now
|
||||
if (!(scenario.equals("send") || scenario.equals("ping"))) {
|
||||
throw new InvalidParameterException("only 'send' and 'ping' are supported for this mode");
|
||||
}
|
||||
|
||||
AppViewModel model = new AppViewModel();
|
||||
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
||||
model.setSenderPacketCount(packetCount);
|
||||
model.setSenderPacketSize(packetSize);
|
||||
model.setSenderPacketInterval(packetInterval);
|
||||
|
||||
runScenario(model, "rfcomm-client", scenario);
|
||||
return resultFromModel(model);
|
||||
Runner runner = runScenario(model, "rfcomm-client", scenario);
|
||||
assert runner != null;
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Run a scenario in RFComm Server mode")
|
||||
public JSONObject runRfcommServer(String scenario) throws JSONException {
|
||||
assert (mBluetoothAdapter != null);
|
||||
|
||||
// We only support "receive" and "pong" for this mode for now
|
||||
if (!(scenario.equals("receive") || scenario.equals("pong"))) {
|
||||
throw new InvalidParameterException("only 'receive' and 'pong' are supported for this mode");
|
||||
}
|
||||
|
||||
AppViewModel model = new AppViewModel();
|
||||
|
||||
Runner runner = runScenario(model, "rfcomm-server", scenario);
|
||||
assert runner != null;
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Run a scenario in L2CAP Client mode")
|
||||
public JSONObject runL2capClient(String scenario, String peerBluetoothAddress, int psm, boolean use_2m_phy, int packetCount, int packetSize, int packetInterval) throws JSONException {
|
||||
public JSONObject runL2capClient(String scenario, String peerBluetoothAddress, int psm,
|
||||
boolean use_2m_phy, int packetCount, int packetSize,
|
||||
int packetInterval) throws JSONException {
|
||||
assert (mBluetoothAdapter != null);
|
||||
|
||||
// We only support "send" and "ping" for this mode for now
|
||||
if (!(scenario.equals("send") || scenario.equals("ping"))) {
|
||||
throw new InvalidParameterException("only 'send' and 'ping' are supported for this mode");
|
||||
}
|
||||
|
||||
AppViewModel model = new AppViewModel();
|
||||
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
||||
model.setL2capPsm(psm);
|
||||
@@ -122,8 +221,66 @@ public class AutomationSnippet implements Snippet {
|
||||
model.setSenderPacketSize(packetSize);
|
||||
model.setSenderPacketInterval(packetInterval);
|
||||
|
||||
runScenario(model, "l2cap-client", scenario);
|
||||
return resultFromModel(model);
|
||||
Runner runner = runScenario(model, "l2cap-client", scenario);
|
||||
assert runner != null;
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Run a scenario in L2CAP Server mode")
|
||||
public JSONObject runL2capServer(String scenario) throws JSONException {
|
||||
assert (mBluetoothAdapter != null);
|
||||
|
||||
// We only support "receive" and "pong" for this mode for now
|
||||
if (!(scenario.equals("receive") || scenario.equals("pong"))) {
|
||||
throw new InvalidParameterException("only 'receive' and 'pong' are supported for this mode");
|
||||
}
|
||||
|
||||
AppViewModel model = new AppViewModel();
|
||||
|
||||
Runner runner = runScenario(model, "l2cap-server", scenario);
|
||||
assert runner != null;
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Stop a Runner")
|
||||
public JSONObject stopRunner(String runnerId) throws JSONException {
|
||||
Runner runner = findRunner(runnerId);
|
||||
if (runner == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
runner.stop();
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Wait for a Runner to complete")
|
||||
public JSONObject waitForRunnerCompletion(String runnerId) throws JSONException {
|
||||
Runner runner = findRunner(runnerId);
|
||||
if (runner == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
runner.waitForCompletion();
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Get a Runner by ID")
|
||||
public JSONObject getRunner(String runnerId) throws JSONException {
|
||||
Runner runner = findRunner(runnerId);
|
||||
if (runner == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
return runner.toJson();
|
||||
}
|
||||
|
||||
@Rpc(description = "Get all Runners")
|
||||
public JSONObject getRunners() throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
JSONArray runners = new JSONArray();
|
||||
result.put("runners", runners);
|
||||
for (Runner runner: mRunners) {
|
||||
runners.put(runner.toJson());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,8 +32,10 @@ class L2capClient(
|
||||
private val context: Context,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) : Mode {
|
||||
private var socketClient: SocketClient? = null
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun run(blocking: Boolean) {
|
||||
override fun run() {
|
||||
viewModel.running = true
|
||||
val addressIsPublic = viewModel.peerBluetoothAddress.endsWith("/P")
|
||||
val address = viewModel.peerBluetoothAddress.take(17)
|
||||
@@ -97,7 +99,11 @@ class L2capClient(
|
||||
|
||||
val socket = remoteDevice.createInsecureL2capChannel(viewModel.l2capPsm)
|
||||
|
||||
val client = SocketClient(viewModel, socket, createIoClient)
|
||||
client.run(blocking)
|
||||
socketClient = SocketClient(viewModel, socket, createIoClient)
|
||||
socketClient!!.run()
|
||||
}
|
||||
|
||||
override fun waitForCompletion() {
|
||||
socketClient?.waitForCompletion()
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,10 @@ class L2capServer(
|
||||
private val bluetoothAdapter: BluetoothAdapter,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) : Mode {
|
||||
private var socketServer: SocketServer? = null
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun run(blocking: Boolean) {
|
||||
override fun run() {
|
||||
// Advertise so that the peer can find us and connect.
|
||||
val callback = object : AdvertiseCallback() {
|
||||
override fun onStartFailure(errorCode: Int) {
|
||||
@@ -59,11 +61,14 @@ class L2capServer(
|
||||
viewModel.l2capPsm = serverSocket.psm
|
||||
Log.info("psm = $serverSocket.psm")
|
||||
|
||||
val server = SocketServer(viewModel, serverSocket, createIoClient)
|
||||
server.run(
|
||||
socketServer = SocketServer(viewModel, serverSocket, createIoClient)
|
||||
socketServer!!.run(
|
||||
{ advertiser.stopAdvertising(callback) },
|
||||
{ advertiser.startAdvertising(advertiseSettings, advertiseData, scanData, callback) },
|
||||
blocking
|
||||
{ advertiser.startAdvertising(advertiseSettings, advertiseData, scanData, callback) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun waitForCompletion() {
|
||||
socketServer?.waitForCompletion()
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ class MainActivity : ComponentActivity() {
|
||||
L2CAP_SERVER_MODE -> L2capServer(appViewModel, bluetoothAdapter!!, ::createIoClient)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
runner.run(false)
|
||||
runner.run()
|
||||
}
|
||||
|
||||
private fun runScan(startScan: Boolean) {
|
||||
@@ -443,6 +443,12 @@ fun MainView(
|
||||
Text(
|
||||
text = if (appViewModel.rxPhy != 0 || appViewModel.txPhy != 0) "PHY: tx=${appViewModel.txPhy}, rx=${appViewModel.rxPhy}" else ""
|
||||
)
|
||||
Text(
|
||||
text = "Status: ${appViewModel.status}"
|
||||
)
|
||||
Text(
|
||||
text = "Last Error: ${appViewModel.lastError}"
|
||||
)
|
||||
Text(
|
||||
text = "Packets Sent: ${appViewModel.packetsSent}"
|
||||
)
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
package com.github.google.bumble.btbench
|
||||
|
||||
interface Mode {
|
||||
fun run(blocking: Boolean)
|
||||
fun run()
|
||||
fun waitForCompletion()
|
||||
}
|
||||
@@ -42,6 +42,8 @@ const val PONG_SCENARIO = "Pong"
|
||||
|
||||
class AppViewModel : ViewModel() {
|
||||
private var preferences: SharedPreferences? = null
|
||||
var status by mutableStateOf("")
|
||||
var lastError by mutableStateOf("")
|
||||
var mode by mutableStateOf(RFCOMM_SERVER_MODE)
|
||||
var scenario by mutableStateOf(RECEIVE_SCENARIO)
|
||||
var peerBluetoothAddress by mutableStateOf(DEFAULT_PEER_BLUETOOTH_ADDRESS)
|
||||
@@ -218,6 +220,18 @@ class AppViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
status = ""
|
||||
lastError = ""
|
||||
mtu = 0
|
||||
rxPhy = 0
|
||||
txPhy = 0
|
||||
packetsSent = 0
|
||||
packetsReceived = 0
|
||||
throughput = 0
|
||||
stats = ""
|
||||
}
|
||||
|
||||
fun abort() {
|
||||
aborter?.let { it() }
|
||||
}
|
||||
|
||||
@@ -34,9 +34,7 @@ class Pinger(private val viewModel: AppViewModel, private val packetIO: PacketIO
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
viewModel.packetsSent = 0
|
||||
viewModel.packetsReceived = 0
|
||||
viewModel.stats = ""
|
||||
viewModel.clear()
|
||||
|
||||
Log.info("startup delay: $DEFAULT_STARTUP_DELAY")
|
||||
Thread.sleep(DEFAULT_STARTUP_DELAY.toLong());
|
||||
|
||||
@@ -28,7 +28,9 @@ class Ponger(private val viewModel: AppViewModel, private val packetIO: PacketIO
|
||||
packetIO.packetSink = this
|
||||
}
|
||||
|
||||
override fun run() {}
|
||||
override fun run() {
|
||||
viewModel.clear()
|
||||
}
|
||||
|
||||
override fun abort() {}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ class Receiver(private val viewModel: AppViewModel, private val packetIO: Packet
|
||||
packetIO.packetSink = this
|
||||
}
|
||||
|
||||
override fun run() {}
|
||||
override fun run() {
|
||||
viewModel.clear()
|
||||
}
|
||||
|
||||
override fun abort() {}
|
||||
|
||||
|
||||
@@ -25,15 +25,21 @@ class RfcommClient(
|
||||
private val bluetoothAdapter: BluetoothAdapter,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) : Mode {
|
||||
private var socketClient: SocketClient? = null
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun run(blocking: Boolean) {
|
||||
override fun run() {
|
||||
val address = viewModel.peerBluetoothAddress.take(17)
|
||||
val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
|
||||
val socket = remoteDevice.createInsecureRfcommSocketToServiceRecord(
|
||||
DEFAULT_RFCOMM_UUID
|
||||
)
|
||||
|
||||
val client = SocketClient(viewModel, socket, createIoClient)
|
||||
client.run(blocking)
|
||||
socketClient = SocketClient(viewModel, socket, createIoClient)
|
||||
socketClient!!.run()
|
||||
}
|
||||
|
||||
override fun waitForCompletion() {
|
||||
socketClient?.waitForCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ package com.github.google.bumble.btbench
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import java.io.IOException
|
||||
import java.util.logging.Logger
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
private val Log = Logger.getLogger("btbench.rfcomm-server")
|
||||
|
||||
@@ -27,13 +25,18 @@ class RfcommServer(
|
||||
private val bluetoothAdapter: BluetoothAdapter,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) : Mode {
|
||||
private var socketServer: SocketServer? = null
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun run(blocking: Boolean) {
|
||||
override fun run() {
|
||||
val serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
|
||||
"BumbleBench", DEFAULT_RFCOMM_UUID
|
||||
)
|
||||
socketServer = SocketServer(viewModel, serverSocket, createIoClient)
|
||||
socketServer!!.run({}, {})
|
||||
}
|
||||
|
||||
val server = SocketServer(viewModel, serverSocket, createIoClient)
|
||||
server.run({}, {}, blocking)
|
||||
override fun waitForCompletion() {
|
||||
socketServer?.waitForCompletion()
|
||||
}
|
||||
}
|
||||
@@ -34,10 +34,7 @@ class Sender(private val viewModel: AppViewModel, private val packetIO: PacketIO
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
viewModel.packetsSent = 0
|
||||
viewModel.packetsReceived = 0
|
||||
viewModel.throughput = 0
|
||||
viewModel.stats = ""
|
||||
viewModel.clear()
|
||||
|
||||
Log.info("startup delay: $DEFAULT_STARTUP_DELAY")
|
||||
Thread.sleep(DEFAULT_STARTUP_DELAY.toLong());
|
||||
|
||||
@@ -27,8 +27,10 @@ class SocketClient(
|
||||
private val socket: BluetoothSocket,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) {
|
||||
private var clientThread: Thread? = null
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun run(blocking: Boolean = false) {
|
||||
fun run() {
|
||||
viewModel.running = true
|
||||
val socketDataSink = SocketDataSink(socket)
|
||||
val streamIO = StreamedPacketIO(socketDataSink)
|
||||
@@ -41,7 +43,7 @@ class SocketClient(
|
||||
viewModel.running = false
|
||||
}
|
||||
|
||||
val clientThread = thread(name = "SocketClient") {
|
||||
clientThread = thread(name = "SocketClient") {
|
||||
viewModel.aborter = {
|
||||
ioClient.abort()
|
||||
socket.close()
|
||||
@@ -51,6 +53,8 @@ class SocketClient(
|
||||
socket.connect()
|
||||
} catch (error: IOException) {
|
||||
Log.warning("connection failed")
|
||||
viewModel.status = "ABORTED"
|
||||
viewModel.lastError = "CONNECTION_FAILED"
|
||||
cleanup()
|
||||
return@thread
|
||||
}
|
||||
@@ -65,8 +69,11 @@ class SocketClient(
|
||||
try {
|
||||
ioClient.run()
|
||||
socket.close()
|
||||
viewModel.status = "OK"
|
||||
} catch (error: IOException) {
|
||||
Log.info("run ended abruptly")
|
||||
viewModel.status = "ABORTED"
|
||||
viewModel.lastError = "IO_ERROR"
|
||||
}
|
||||
|
||||
Log.info("waiting for source thread to finish")
|
||||
@@ -74,9 +81,9 @@ class SocketClient(
|
||||
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
if (blocking) {
|
||||
clientThread.join()
|
||||
}
|
||||
fun waitForCompletion() {
|
||||
clientThread?.join()
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,9 @@ class SocketServer(
|
||||
private val serverSocket: BluetoothServerSocket,
|
||||
private val createIoClient: (packetIo: PacketIO) -> IoClient
|
||||
) {
|
||||
fun run(onConnected: () -> Unit, onDisconnected: () -> Unit, blocking: Boolean = false) {
|
||||
private var serverThread: Thread? = null
|
||||
|
||||
fun run(onConnected: () -> Unit, onDisconnected: () -> Unit) {
|
||||
var aborted = false
|
||||
viewModel.running = true
|
||||
|
||||
@@ -35,7 +37,7 @@ class SocketServer(
|
||||
viewModel.running = false
|
||||
}
|
||||
|
||||
val serverThread = thread(name = "SocketServer") {
|
||||
serverThread = thread(name = "SocketServer") {
|
||||
while (!aborted) {
|
||||
viewModel.aborter = {
|
||||
serverSocket.close()
|
||||
@@ -63,15 +65,22 @@ class SocketServer(
|
||||
val socketDataSink = SocketDataSink(socket)
|
||||
val streamIO = StreamedPacketIO(socketDataSink)
|
||||
val socketDataSource = SocketDataSource(socket, streamIO::onData)
|
||||
val ioClient = createIoClient(streamIO)
|
||||
|
||||
val ioThread = thread(name = "IoClient") {
|
||||
val ioClient = createIoClient(streamIO)
|
||||
ioClient.run()
|
||||
}
|
||||
|
||||
socketDataSource.receive()
|
||||
socket.close()
|
||||
ioThread.join()
|
||||
}
|
||||
cleanup()
|
||||
}
|
||||
|
||||
if (blocking) {
|
||||
serverThread.join()
|
||||
}
|
||||
}
|
||||
|
||||
fun waitForCompletion() {
|
||||
serverThread?.join()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user