All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fireflysource.net.tcp.aio.AioTcpClient.kt Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.fireflysource.net.tcp.aio

import com.fireflysource.common.lifecycle.AbstractLifeCycle
import com.fireflysource.common.sys.SystemLogger
import com.fireflysource.net.tcp.TcpChannelGroup
import com.fireflysource.net.tcp.TcpClient
import com.fireflysource.net.tcp.TcpConnection
import com.fireflysource.net.tcp.secure.DefaultSecureEngineFactorySelector
import com.fireflysource.net.tcp.secure.SecureEngineFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.net.SocketAddress
import java.net.StandardSocketOptions
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.CompletionHandler
import java.util.concurrent.CompletableFuture


/**
 * @author Pengtao Qiu
 */
class AioTcpClient(private val config: TcpConfig = TcpConfig()) : AbstractLifeCycle(), TcpClient {

    companion object {
        private val log = SystemLogger.create(AioTcpClient::class.java)
    }

    private var secureEngineFactory: SecureEngineFactory =
        DefaultSecureEngineFactorySelector.createSecureEngineFactory(true)
    private var group: TcpChannelGroup = AioTcpChannelGroup("aio-tcp-client")
    private var stopGroup = true

    override fun init() {
        group.start()
    }

    override fun destroy() {
        if (stopGroup) group.stop()
    }

    override fun tcpChannelGroup(group: TcpChannelGroup): TcpClient {
        this.group = group
        return this
    }

    override fun stopTcpChannelGroup(stop: Boolean): TcpClient {
        this.stopGroup = stop
        return this
    }

    override fun secureEngineFactory(secureEngineFactory: SecureEngineFactory): TcpClient {
        this.secureEngineFactory = secureEngineFactory
        return this
    }

    override fun enableSecureConnection(): TcpClient {
        config.enableSecureConnection = true
        return this
    }

    override fun timeout(timeout: Long): TcpClient {
        config.timeout = timeout
        return this
    }

    override fun bufferSize(bufferSize: Int): TcpClient {
        config.outputBufferSize = bufferSize
        return this
    }

    override fun enableOutputBuffer(): TcpClient {
        config.enableOutputBuffer = true
        return this
    }

    override fun connect(address: SocketAddress): CompletableFuture =
        connect(address, defaultSupportedProtocols)

    override fun connect(address: SocketAddress, supportedProtocols: List): CompletableFuture =
        connect(address, "", 0, supportedProtocols)

    override fun connect(
        address: SocketAddress,
        peerHost: String,
        peerPort: Int,
        supportedProtocols: List
    ): CompletableFuture {
        val future = CompletableFuture()
        try {
            connect(address, peerHost, peerPort, supportedProtocols, future)
        } catch (e: Exception) {
            log.warn(e) { "connecting exception. $address" }
            future.completeExceptionally(e)
        }
        return future
    }

    private fun connect(
        address: SocketAddress,
        peerHost: String,
        peerPort: Int,
        supportedProtocols: List,
        future: CompletableFuture
    ) {
        start()

        fun createSecureEngine(scope: CoroutineScope) = if (peerHost.isNotBlank() && peerPort != 0) {
            secureEngineFactory.create(scope, true, peerHost, peerPort, supportedProtocols)
        } else {
            secureEngineFactory.create(scope, true, supportedProtocols)
        }

        fun createConnection(connectionId: Int, socketChannel: AsynchronousSocketChannel): TcpConnection {
            val aioTcpConnection =
                AioTcpConnection(
                    connectionId,
                    config.timeout,
                    socketChannel,
                    group.getDispatcher(connectionId),
                    config.inputBufferSize
                )

            val tcpConnection = if (config.enableSecureConnection) {
                val secureEngine = createSecureEngine(aioTcpConnection.coroutineScope)
                AioSecureTcpConnection(aioTcpConnection, secureEngine)
            } else aioTcpConnection

            return if (config.enableOutputBuffer) {
                BufferedOutputTcpConnection(tcpConnection, config.outputBufferSize)
            } else tcpConnection
        }

        try {
            val socketChannel = AsynchronousSocketChannel.open(group.asynchronousChannelGroup)
            socketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, config.reuseAddr)
            socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, config.keepAlive)
            socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, config.tcpNoDelay)
            socketChannel.connect(address, group.nextId, object : CompletionHandler {

                override fun completed(result: Void?, connectionId: Int) {
                    try {
                        future.complete(createConnection(connectionId, socketChannel))
                    } catch (e: Exception) {
                        log.warn(e) { "connecting exception. id: ${connectionId}, address: $address" }
                        future.completeExceptionally(e)
                    }
                }

                override fun failed(t: Throwable?, connectionId: Int) {
                    log.warn(t) { "connecting exception. id: ${connectionId}, address: $address" }
                    future.completeExceptionally(t)
                }
            })
        } catch (e: Exception) {
            log.error(e) { "TCP client connect exception" }
            future.completeExceptionally(e)
        }
    }

}

fun TcpClient.connectAsync(host: String, port: Int, block: suspend CoroutineScope.(TcpConnection) -> Unit): TcpClient {
    connect(host, port).thenAccept { connection -> connection.coroutineScope.launch { block(connection) } }
    return this
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy