2023-05-08 09:58:24 +02:00
|
|
|
/*
|
2024-02-05 21:57:40 +01:00
|
|
|
* Copyright 2024 dorkbox, llc
|
2023-05-08 09:58:24 +02:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2023-06-29 01:31:31 +02:00
|
|
|
package dorkbox.network.handshake
|
2023-05-08 09:58:24 +02:00
|
|
|
|
2023-06-14 23:28:34 +02:00
|
|
|
import dorkbox.network.aeron.AeronDriver
|
|
|
|
import dorkbox.network.aeron.AeronDriver.Companion.getLocalAddressString
|
|
|
|
import dorkbox.network.aeron.AeronDriver.Companion.uri
|
2023-06-29 01:31:31 +02:00
|
|
|
import dorkbox.network.aeron.controlEndpoint
|
2023-06-14 23:28:34 +02:00
|
|
|
import dorkbox.network.aeron.endpoint
|
2023-10-28 20:55:49 +02:00
|
|
|
import dorkbox.network.connection.EndPoint
|
2023-05-08 09:58:24 +02:00
|
|
|
import dorkbox.network.exceptions.ClientRetryException
|
|
|
|
import dorkbox.network.exceptions.ClientTimedOutException
|
2023-06-25 17:21:25 +02:00
|
|
|
import io.aeron.CommonContext
|
2023-05-08 09:58:24 +02:00
|
|
|
import java.net.Inet4Address
|
|
|
|
import java.net.InetAddress
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set up the subscription + publication channels to the server
|
|
|
|
*
|
2023-06-14 23:28:34 +02:00
|
|
|
* Note: this class is NOT closed the traditional way! It's pub/sub objects are used by the connection (which is where they are closed)
|
|
|
|
*
|
2023-05-08 09:58:24 +02:00
|
|
|
* @throws ClientRetryException if we need to retry to connect
|
|
|
|
* @throws ClientTimedOutException if we cannot connect to the server in the designated time
|
|
|
|
*/
|
2023-06-19 14:03:18 +02:00
|
|
|
internal class ClientConnectionDriver(val connectionInfo: PubSub) {
|
2023-06-14 23:28:34 +02:00
|
|
|
|
|
|
|
companion object {
|
2023-09-04 00:47:46 +02:00
|
|
|
fun build(
|
2023-06-14 23:28:34 +02:00
|
|
|
aeronDriver: AeronDriver,
|
2023-07-03 19:18:34 +02:00
|
|
|
handshakeTimeoutNs: Long,
|
2023-06-14 23:28:34 +02:00
|
|
|
handshakeConnection: ClientHandshakeDriver,
|
2023-07-11 00:11:58 +02:00
|
|
|
connectionInfo: ClientConnectionInfo,
|
|
|
|
port2Server: Int, // this is the port2 value from the server
|
2023-10-28 20:55:49 +02:00
|
|
|
tagName: String
|
2023-06-14 23:28:34 +02:00
|
|
|
): ClientConnectionDriver {
|
2023-06-19 14:03:18 +02:00
|
|
|
val handshakePubSub = handshakeConnection.pubSub
|
|
|
|
val reliable = handshakePubSub.reliable
|
2023-06-14 23:28:34 +02:00
|
|
|
|
|
|
|
// flipped because we are connecting to these!
|
|
|
|
val sessionIdPub = connectionInfo.sessionIdSub
|
|
|
|
val sessionIdSub = connectionInfo.sessionIdPub
|
|
|
|
val streamIdPub = connectionInfo.streamIdSub
|
|
|
|
val streamIdSub = connectionInfo.streamIdPub
|
|
|
|
|
2023-06-19 14:03:18 +02:00
|
|
|
val isUsingIPC = handshakePubSub.isIpc
|
2023-06-14 23:28:34 +02:00
|
|
|
|
|
|
|
val logInfo: String
|
|
|
|
|
|
|
|
val pubSub: PubSub
|
|
|
|
|
|
|
|
if (isUsingIPC) {
|
|
|
|
// Create a subscription at the given address and port, using the given stream ID.
|
|
|
|
logInfo = "CONNECTION-IPC"
|
|
|
|
|
|
|
|
pubSub = buildIPC(
|
|
|
|
aeronDriver = aeronDriver,
|
2023-07-03 19:18:34 +02:00
|
|
|
handshakeTimeoutNs = handshakeTimeoutNs,
|
2023-06-14 23:28:34 +02:00
|
|
|
sessionIdPub = sessionIdPub,
|
|
|
|
sessionIdSub = sessionIdSub,
|
|
|
|
streamIdPub = streamIdPub,
|
|
|
|
streamIdSub = streamIdSub,
|
|
|
|
reliable = reliable,
|
2023-10-28 20:55:49 +02:00
|
|
|
tagName = tagName,
|
2023-06-14 23:28:34 +02:00
|
|
|
logInfo = logInfo
|
|
|
|
)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
2023-06-14 23:28:34 +02:00
|
|
|
else {
|
2023-06-19 14:03:18 +02:00
|
|
|
val remoteAddress = handshakePubSub.remoteAddress
|
|
|
|
val remoteAddressString = handshakePubSub.remoteAddressString
|
|
|
|
val portPub = handshakePubSub.portPub
|
|
|
|
val portSub = handshakePubSub.portSub
|
2023-06-14 23:28:34 +02:00
|
|
|
|
|
|
|
logInfo = if (remoteAddress is Inet4Address) {
|
|
|
|
"CONNECTION-IPv4"
|
|
|
|
} else {
|
|
|
|
"CONNECTION-IPv6"
|
|
|
|
}
|
|
|
|
|
|
|
|
pubSub = buildUDP(
|
|
|
|
aeronDriver = aeronDriver,
|
2023-07-03 19:18:34 +02:00
|
|
|
handshakeTimeoutNs = handshakeTimeoutNs,
|
2023-06-14 23:28:34 +02:00
|
|
|
sessionIdPub = sessionIdPub,
|
|
|
|
sessionIdSub = sessionIdSub,
|
|
|
|
streamIdPub = streamIdPub,
|
|
|
|
streamIdSub = streamIdSub,
|
|
|
|
remoteAddress = remoteAddress!!,
|
|
|
|
remoteAddressString = remoteAddressString,
|
2023-06-19 14:03:18 +02:00
|
|
|
portPub = portPub,
|
|
|
|
portSub = portSub,
|
2023-07-11 00:11:58 +02:00
|
|
|
port2Server = port2Server,
|
2023-06-14 23:28:34 +02:00
|
|
|
reliable = reliable,
|
2023-10-28 20:55:49 +02:00
|
|
|
tagName = tagName,
|
2023-06-14 23:28:34 +02:00
|
|
|
logInfo = logInfo
|
|
|
|
)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
|
2023-06-19 14:03:18 +02:00
|
|
|
return ClientConnectionDriver(pubSub)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
|
2023-06-14 23:28:34 +02:00
|
|
|
@Throws(ClientTimedOutException::class)
|
2023-09-04 00:47:46 +02:00
|
|
|
private fun buildIPC(
|
2023-06-14 23:28:34 +02:00
|
|
|
aeronDriver: AeronDriver,
|
2023-07-03 19:18:34 +02:00
|
|
|
handshakeTimeoutNs: Long,
|
2023-06-14 23:28:34 +02:00
|
|
|
sessionIdPub: Int,
|
|
|
|
sessionIdSub: Int,
|
|
|
|
streamIdPub: Int,
|
|
|
|
streamIdSub: Int,
|
|
|
|
reliable: Boolean,
|
2023-10-28 20:55:49 +02:00
|
|
|
tagName: String,
|
2023-06-14 23:28:34 +02:00
|
|
|
logInfo: String
|
|
|
|
): PubSub {
|
|
|
|
// on close, the publication CAN linger (in case a client goes away, and then comes back)
|
|
|
|
// AERON_PUBLICATION_LINGER_TIMEOUT, 5s by default (this can also be set as a URI param)
|
|
|
|
|
|
|
|
// Create a publication at the given address and port, using the given stream ID.
|
2023-06-25 17:21:25 +02:00
|
|
|
val publicationUri = uri(CommonContext.IPC_MEDIA, sessionIdPub, reliable)
|
2023-06-14 23:28:34 +02:00
|
|
|
|
|
|
|
// NOTE: Handlers are called on the client conductor thread. The client conductor thread expects handlers to do safe
|
|
|
|
// publication of any state to other threads and not be long running or re-entrant with the client.
|
2023-06-28 15:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
// can throw an exception! We catch it in the calling class
|
2023-07-21 00:19:31 +02:00
|
|
|
val publication = aeronDriver.addPublication(publicationUri, streamIdPub, logInfo, true)
|
2023-06-28 15:09:14 +02:00
|
|
|
|
|
|
|
// can throw an exception! We catch it in the calling class
|
|
|
|
// we actually have to wait for it to connect before we continue
|
2023-07-03 19:18:34 +02:00
|
|
|
aeronDriver.waitForConnection(publication, handshakeTimeoutNs, logInfo) { cause ->
|
2023-06-14 23:28:34 +02:00
|
|
|
ClientTimedOutException("$logInfo publication cannot connect with server!", cause)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
|
2023-06-28 15:09:14 +02:00
|
|
|
|
2023-06-14 23:28:34 +02:00
|
|
|
// Create a subscription at the given address and port, using the given stream ID.
|
2023-06-25 17:21:25 +02:00
|
|
|
val subscriptionUri = uri(CommonContext.IPC_MEDIA, sessionIdSub, reliable)
|
2023-07-01 22:51:20 +02:00
|
|
|
val subscription = aeronDriver.addSubscription(subscriptionUri, streamIdSub, logInfo, true)
|
2023-05-08 09:58:24 +02:00
|
|
|
|
2023-07-22 14:18:38 +02:00
|
|
|
|
|
|
|
// wait for the REMOTE end to also connect to us!
|
|
|
|
aeronDriver.waitForConnection(subscription, handshakeTimeoutNs, logInfo) { cause ->
|
|
|
|
ClientTimedOutException("$logInfo subscription cannot connect with server!", cause)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-28 20:55:49 +02:00
|
|
|
return PubSub(
|
|
|
|
pub = publication,
|
|
|
|
sub = subscription,
|
|
|
|
sessionIdPub = sessionIdPub,
|
|
|
|
sessionIdSub = sessionIdSub,
|
|
|
|
streamIdPub = streamIdPub,
|
|
|
|
streamIdSub = streamIdSub,
|
|
|
|
reliable = reliable,
|
|
|
|
remoteAddress = null,
|
|
|
|
remoteAddressString = EndPoint.IPC_NAME,
|
|
|
|
portPub = 0,
|
|
|
|
portSub = 0,
|
|
|
|
tagName = tagName
|
|
|
|
)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
|
2023-06-14 23:28:34 +02:00
|
|
|
@Throws(ClientTimedOutException::class)
|
2023-09-04 00:47:46 +02:00
|
|
|
private fun buildUDP(
|
2023-06-14 23:28:34 +02:00
|
|
|
aeronDriver: AeronDriver,
|
2023-07-03 19:18:34 +02:00
|
|
|
handshakeTimeoutNs: Long,
|
2023-06-14 23:28:34 +02:00
|
|
|
sessionIdPub: Int,
|
|
|
|
sessionIdSub: Int,
|
|
|
|
streamIdPub: Int,
|
|
|
|
streamIdSub: Int,
|
|
|
|
remoteAddress: InetAddress,
|
|
|
|
remoteAddressString: String,
|
|
|
|
portPub: Int,
|
|
|
|
portSub: Int,
|
2023-07-11 00:11:58 +02:00
|
|
|
port2Server: Int, // this is the port2 value from the server
|
2023-06-14 23:28:34 +02:00
|
|
|
reliable: Boolean,
|
2023-10-28 20:55:49 +02:00
|
|
|
tagName: String,
|
2023-06-14 23:28:34 +02:00
|
|
|
logInfo: String,
|
|
|
|
): PubSub {
|
|
|
|
val isRemoteIpv4 = remoteAddress is Inet4Address
|
|
|
|
|
|
|
|
// on close, the publication CAN linger (in case a client goes away, and then comes back)
|
|
|
|
// AERON_PUBLICATION_LINGER_TIMEOUT, 5s by default (this can also be set as a URI param)
|
|
|
|
|
|
|
|
// Create a publication at the given address and port, using the given stream ID.
|
2023-06-25 17:21:25 +02:00
|
|
|
val publicationUri = uri(CommonContext.UDP_MEDIA, sessionIdPub, reliable)
|
2023-06-14 23:28:34 +02:00
|
|
|
.endpoint(isRemoteIpv4, remoteAddressString, portPub)
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: Handlers are called on the client conductor thread. The client conductor thread expects handlers to do safe
|
|
|
|
// publication of any state to other threads and not be long running or re-entrant with the client.
|
2023-06-28 15:09:14 +02:00
|
|
|
|
|
|
|
// can throw an exception! We catch it in the calling class
|
2023-07-21 00:19:31 +02:00
|
|
|
val publication = aeronDriver.addPublication(publicationUri, streamIdPub, logInfo, false)
|
2023-06-28 15:09:14 +02:00
|
|
|
|
|
|
|
// can throw an exception! We catch it in the calling class
|
|
|
|
// we actually have to wait for it to connect before we continue
|
2023-07-03 19:18:34 +02:00
|
|
|
aeronDriver.waitForConnection(publication, handshakeTimeoutNs, logInfo) { cause ->
|
2023-06-14 23:28:34 +02:00
|
|
|
ClientTimedOutException("$logInfo publication cannot connect with server $remoteAddressString", cause)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
|
2023-06-14 23:28:34 +02:00
|
|
|
// this will cause us to listen on the interface that connects with the remote address, instead of ALL interfaces.
|
2023-06-29 23:50:22 +02:00
|
|
|
val localAddressString = getLocalAddressString(publication, isRemoteIpv4)
|
2023-05-08 09:58:24 +02:00
|
|
|
|
2023-06-29 01:31:31 +02:00
|
|
|
|
|
|
|
// A control endpoint for the subscriptions will cause a periodic service management "heartbeat" to be sent to the
|
|
|
|
// remote endpoint publication, which permits the remote publication to send us data, thereby getting us around NAT
|
2023-06-25 17:21:25 +02:00
|
|
|
val subscriptionUri = uri(CommonContext.UDP_MEDIA, sessionIdSub, reliable)
|
2023-07-11 00:11:58 +02:00
|
|
|
.endpoint(isRemoteIpv4, localAddressString, portSub)
|
|
|
|
.controlEndpoint(isRemoteIpv4, remoteAddressString, port2Server)
|
2023-06-29 01:31:31 +02:00
|
|
|
.controlMode(CommonContext.MDC_CONTROL_MODE_DYNAMIC)
|
2023-05-08 09:58:24 +02:00
|
|
|
|
2023-07-01 22:51:20 +02:00
|
|
|
val subscription = aeronDriver.addSubscription(subscriptionUri, streamIdSub, logInfo, false)
|
2023-05-08 09:58:24 +02:00
|
|
|
|
2023-06-29 01:31:31 +02:00
|
|
|
|
2023-07-22 14:18:38 +02:00
|
|
|
// wait for the REMOTE end to also connect to us!
|
|
|
|
aeronDriver.waitForConnection(subscription, handshakeTimeoutNs, logInfo) { cause ->
|
|
|
|
ClientTimedOutException("$logInfo subscription cannot connect with server!", cause)
|
|
|
|
}
|
2023-06-29 01:31:31 +02:00
|
|
|
|
2023-10-28 20:55:49 +02:00
|
|
|
return PubSub(
|
|
|
|
pub = publication,
|
|
|
|
sub = subscription,
|
|
|
|
sessionIdPub = sessionIdPub,
|
|
|
|
sessionIdSub = sessionIdSub,
|
|
|
|
streamIdPub = streamIdPub,
|
|
|
|
streamIdSub = streamIdSub,
|
|
|
|
reliable = reliable,
|
|
|
|
remoteAddress = remoteAddress,
|
|
|
|
remoteAddressString = remoteAddressString,
|
|
|
|
portPub = portPub,
|
|
|
|
portSub = portSub,
|
|
|
|
tagName = tagName
|
|
|
|
)
|
2023-05-08 09:58:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|