started more or less MP internet protocols
This commit is contained in:
parent
770555024f
commit
6c10c2e578
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -4,7 +4,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="17 (5)" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="17 (5)" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -17,7 +17,7 @@ repositories {
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm {
|
jvm {
|
||||||
jvmToolchain(11)
|
jvmToolchain(8)
|
||||||
withJava()
|
withJava()
|
||||||
testRuns.named("test") {
|
testRuns.named("test") {
|
||||||
executionTask.configure {
|
executionTask.configure {
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplatform implementation of an internet address.
|
||||||
|
* Notice to implementors. It must provide correct and effective [equals] and [hashCode].
|
||||||
|
*/
|
||||||
|
interface NetworkAddress {
|
||||||
|
val host: String
|
||||||
|
val port: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplatform datagram abstraction
|
||||||
|
*/
|
||||||
|
interface Datagram {
|
||||||
|
val message: UByteArray
|
||||||
|
val address: NetworkAddress
|
||||||
|
suspend fun respondWith(message: UByteArray)
|
||||||
|
}
|
||||||
|
interface DatagramReceiver {
|
||||||
|
|
||||||
|
val incoming: ReceiveChannel<Datagram>
|
||||||
|
suspend fun send(message: UByteArray, networkAddress: NetworkAddress)
|
||||||
|
@Suppress("unused")
|
||||||
|
suspend fun send(message: UByteArray, datagramAddress: String) {
|
||||||
|
send(message, networkAddressOf(datagramAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun send(message: UByteArray,host: String,port: Int) =
|
||||||
|
send(message, NetworkAddress(host,port))
|
||||||
|
fun close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect fun networkAddressOf(address: String): NetworkAddress
|
||||||
|
expect fun NetworkAddress(host: String,port: Int): NetworkAddress
|
@ -0,0 +1,5 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
actual fun networkAddressOf(address: String): NetworkAddress {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
actual fun NetworkAddress(host: String, port: Int): NetworkAddress {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
|
actual fun networkAddressOf(address: String): NetworkAddress {
|
||||||
|
val (host,port) = address.split(":")
|
||||||
|
return JvmNetworkAddress(InetAddress.getByName(host), port.toInt())
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
|
actual fun NetworkAddress(host: String, port: Int): NetworkAddress =
|
||||||
|
JvmNetworkAddress(InetAddress.getByName(host), port)
|
135
src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/UdpServer.kt
Normal file
135
src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/UdpServer.kt
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import net.sergeych.mp_logger.LogTag
|
||||||
|
import net.sergeych.mp_logger.exception
|
||||||
|
import net.sergeych.mp_logger.info
|
||||||
|
import net.sergeych.mp_logger.warning
|
||||||
|
import java.net.DatagramPacket
|
||||||
|
import java.net.DatagramSocket
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
private val counter = AtomicInteger(0)
|
||||||
|
|
||||||
|
class JvmNetworkAddress(val inetAddress: InetAddress, override val port: Int) : NetworkAddress {
|
||||||
|
override val host: String by lazy { inetAddress.hostName }
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is JvmNetworkAddress) return false
|
||||||
|
|
||||||
|
if (inetAddress != other.inetAddress) return false
|
||||||
|
if (port != other.port) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = inetAddress.hashCode()
|
||||||
|
result = 31 * result + port
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class UdpDatagram(override val message: UByteArray, val inetAddress: InetAddress, val port: Int) : Datagram {
|
||||||
|
|
||||||
|
override val address: NetworkAddress by lazy {
|
||||||
|
JvmNetworkAddress(inetAddress, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val access = Mutex()
|
||||||
|
|
||||||
|
private var socket: DatagramSocket? = null
|
||||||
|
override suspend fun respondWith(message: UByteArray) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
access.withLock {
|
||||||
|
if (socket == null) socket = DatagramSocket()
|
||||||
|
val packet = DatagramPacket(
|
||||||
|
message.toByteArray(),
|
||||||
|
message.size,
|
||||||
|
inetAddress,
|
||||||
|
port
|
||||||
|
)
|
||||||
|
socket!!.send(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
class UdpServer(val port: Int) :
|
||||||
|
DatagramReceiver, LogTag("UDPS:${counter.incrementAndGet()}") {
|
||||||
|
private var isClosed = false
|
||||||
|
|
||||||
|
|
||||||
|
private val deferredSocket = CompletableDeferred<DatagramSocket>()
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
|
private suspend fun start() = try {
|
||||||
|
coroutineScope {
|
||||||
|
val socket = DatagramSocket(port)
|
||||||
|
val buffer = ByteArray(16384)
|
||||||
|
val packet = DatagramPacket(buffer, buffer.size)
|
||||||
|
deferredSocket.complete(socket)
|
||||||
|
while (isActive && !isClosed) {
|
||||||
|
try {
|
||||||
|
socket.receive(packet)
|
||||||
|
val data = packet.data.sliceArray(0..<packet.length)
|
||||||
|
val datagram = UdpDatagram(data.toUByteArray(), packet.address, packet.port)
|
||||||
|
if (!channel.trySend(datagram).isSuccess) {
|
||||||
|
warning { "packet lost!" }
|
||||||
|
// and we cause overflow that overwrites the oldest
|
||||||
|
channel.send(datagram)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (!isClosed)
|
||||||
|
e.printStackTrace()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info { "closing socket and reception loop" }
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (_: CancellationException) {
|
||||||
|
info { "server is closed" }
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
exception { "unexpected end of server" to t }
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
job = GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (!isClosed) {
|
||||||
|
if (deferredSocket.isCompleted) {
|
||||||
|
runCatching {
|
||||||
|
deferredSocket.getCompleted().close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClosed = true
|
||||||
|
job?.cancel(); job = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val channel = Channel<Datagram>(2048, BufferOverflow.DROP_OLDEST)
|
||||||
|
override val incoming = channel
|
||||||
|
|
||||||
|
override suspend fun send(message: UByteArray, networkAddress: NetworkAddress) {
|
||||||
|
networkAddress as JvmNetworkAddress
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val packet = DatagramPacket(
|
||||||
|
message.toByteArray(), message.size,
|
||||||
|
networkAddress.inetAddress, networkAddress.port
|
||||||
|
)
|
||||||
|
deferredSocket.await().send(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapters
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import net.sergeych.kiloparsec.adapter.UdpServer
|
||||||
|
import net.sergeych.mp_logger.Log
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class UServerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun udpProvider() = runTest {
|
||||||
|
Log.connectConsole(Log.Level.DEBUG)
|
||||||
|
val s1 = UdpServer(17120)
|
||||||
|
val s2 = UdpServer(17121)
|
||||||
|
s1.send("Hello".encodeToUByteArray(), "localhost",17121)
|
||||||
|
val d1 = s2.incoming.receive()
|
||||||
|
assertEquals(d1.address.port, 17120)
|
||||||
|
assertEquals("Hello", d1.message.toByteArray().decodeToString())
|
||||||
|
d1.respondWith("world".encodeToUByteArray())
|
||||||
|
assertEquals("world", s1.incoming.receive().message.toByteArray().decodeToString())
|
||||||
|
// println("s1: ${s1.bindAddress()}")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
actual fun networkAddressOf(address: String): NetworkAddress {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package net.sergeych.kiloparsec.adapter
|
||||||
|
|
||||||
|
actual fun NetworkAddress(host: String, port: Int): NetworkAddress {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user