Ktor raw сокеты читают канал, ничего не читая сразу после установления соединения - PullRequest
1 голос
/ 11 января 2020

Я пытаюсь подключиться к IMAP-серверу с необработанными сокетами Ktor

@KtorExperimentalAPI
    override suspend fun start(host: SocketAddress) {
        socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(host).tls(Dispatchers.IO)
        writeChannel = socket.openWriteChannel()
        readChannel = socket.openReadChannel()
        val handshakeResponse = ByteArray(readChannel.availableForRead)
        readChannel.readAvailable(handshakeResponse)
        val handshake = String(handshakeResponse)
        println(handshake)
    } 

После этого подключения я ожидаю, что получу ответ сервера IMAP, подобный этому

* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE  SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.

И затем я посылаю команды IMAP для входа в мой почтовый ящик

  @KtorExperimentalAPI
    override suspend fun command(command: String): String {
        writeChannel.write("$command\r\n".also { print(it) })
        val response = ByteArray(readChannel.availableForRead)
        readChannel.readAvailable(response)
        val responseString = String(response)
        println(responseString)
        return responseString
    }

Таким образом, мой вывод должен быть таким

 * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE  SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
S1 LOGIN email pass
S1 OK Logged in

Но по какой-то причине вывод выглядит так, как будто эти команды выполняются параллельно threads

S1 LOGIN email pass
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE  SPECIAL-USE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.

И моя сопрограмма заблокирована на каналах записи / чтения, потому что Ktor использует рандомные каналы, и я не могу записать на канал, пока не прочту вывод

Мой основной код выглядит следующим образом

 fun run() {
        val store = IMAPStore()
        runBlocking(coroutineContext) {
                //connect with tls (suspend function)
                store.install()
                //send login command (suspend function)
                store.login()
        }
    }

Я, наверное, не совсем понимаю, как работает соединение через tls и как работают каналы. Может быть, я неправильно запускаю сопрограммы.

В этом примере кода тот же лог c отлично работает

fun main() {
    runBlocking {
        val socket =
            aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(host)
                .tls(Dispatchers.IO)
        val w = socket.openWriteChannel(autoFlush = false)
        val r = socket.openReadChannel()
        r.readUTF8LineTo(System.out)
        w.write("$TAG${tagCounter++} LOGIN email password\r\n")
        r.readUTF8LineTo(System.out)
    }

Может кто-то понять и объяснить это поведение?

...