Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }