Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

Sockets.kt 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package com.dmdirc.ktirc.io
  2. import kotlinx.coroutines.CancellableContinuation
  3. import kotlinx.coroutines.CoroutineScope
  4. import kotlinx.coroutines.io.ByteChannel
  5. import kotlinx.coroutines.io.ByteWriteChannel
  6. import kotlinx.coroutines.io.close
  7. import kotlinx.coroutines.launch
  8. import kotlinx.coroutines.suspendCancellableCoroutine
  9. import kotlinx.io.pool.useInstance
  10. import java.net.SocketAddress
  11. import java.nio.ByteBuffer
  12. import java.nio.channels.*
  13. import kotlin.coroutines.resume
  14. import kotlin.coroutines.resumeWithException
  15. internal interface Socket {
  16. fun bind(socketAddress: SocketAddress)
  17. suspend fun connect(socketAddress: SocketAddress)
  18. suspend fun read(): ByteBuffer?
  19. fun close()
  20. val write: ByteWriteChannel
  21. val isOpen: Boolean
  22. }
  23. internal class PlainTextSocket(private val scope: CoroutineScope) : Socket {
  24. private val client = AsynchronousSocketChannel.open()
  25. private var writeChannel = ByteChannel(autoFlush = true)
  26. override val write: ByteWriteChannel
  27. get() = writeChannel
  28. override val isOpen: Boolean
  29. get() = client.isOpen
  30. override fun bind(socketAddress: SocketAddress) {
  31. client.bind(socketAddress)
  32. }
  33. override suspend fun connect(socketAddress: SocketAddress) {
  34. writeChannel = ByteChannel(autoFlush = true)
  35. suspendCancellableCoroutine<Unit> { continuation ->
  36. client.closeOnCancel(continuation)
  37. client.connect(socketAddress, continuation, AsyncVoidIOHandler)
  38. }
  39. scope.launch { writeLoop() }
  40. }
  41. override fun close() {
  42. writeChannel.close()
  43. client.close()
  44. }
  45. override suspend fun read() = try {
  46. val buffer = byteBufferPool.borrow()
  47. val bytes = suspendCancellableCoroutine<Int> { continuation ->
  48. client.closeOnCancel(continuation)
  49. client.read(buffer, continuation, asyncIOHandler())
  50. }
  51. if (bytes == -1) {
  52. close()
  53. }
  54. buffer.flip()
  55. buffer
  56. } catch (_: ClosedChannelException) {
  57. // Ignore
  58. null
  59. }
  60. private suspend fun writeLoop() {
  61. while (client.isOpen) {
  62. byteBufferPool.useInstance { buffer ->
  63. writeChannel.readAvailable(buffer)
  64. buffer.flip()
  65. try {
  66. suspendCancellableCoroutine<Int> { continuation ->
  67. client.closeOnCancel(continuation)
  68. client.write(buffer, continuation, asyncIOHandler())
  69. }
  70. } catch (_: ClosedChannelException) {
  71. // Ignore
  72. }
  73. }
  74. }
  75. }
  76. }
  77. private fun Channel.closeOnCancel(cont: CancellableContinuation<*>) {
  78. cont.invokeOnCancellation {
  79. try {
  80. close()
  81. } catch (ex: Throwable) {
  82. // Specification says that it is Ok to call it any time, but reality is different,
  83. // so we have just to ignore exception
  84. }
  85. }
  86. }
  87. @Suppress("UNCHECKED_CAST")
  88. private fun <T> asyncIOHandler(): CompletionHandler<T, CancellableContinuation<T>> =
  89. AsyncIOHandlerAny as CompletionHandler<T, CancellableContinuation<T>>
  90. private object AsyncIOHandlerAny : CompletionHandler<Any, CancellableContinuation<Any>> {
  91. override fun completed(result: Any, cont: CancellableContinuation<Any>) {
  92. cont.resume(result)
  93. }
  94. override fun failed(ex: Throwable, cont: CancellableContinuation<Any>) {
  95. // just return if already cancelled and got an expected exception for that case
  96. if (ex is AsynchronousCloseException && cont.isCancelled) return
  97. cont.resumeWithException(ex)
  98. }
  99. }
  100. private object AsyncVoidIOHandler : CompletionHandler<Void?, CancellableContinuation<Unit>> {
  101. override fun completed(result: Void?, cont: CancellableContinuation<Unit>) {
  102. cont.resume(Unit)
  103. }
  104. override fun failed(ex: Throwable, cont: CancellableContinuation<Unit>) {
  105. // just return if already cancelled and got an expected exception for that case
  106. if (ex is AsynchronousCloseException && cont.isCancelled) return
  107. cont.resumeWithException(ex)
  108. }
  109. }