diff --git a/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2cfe00e000f65d07a859194c736fd5c98f17d460
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ProtocolInfo.kt
@@ -0,0 +1,26 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid
+
+data class ProtocolInfo(
+  val flags: UByte,
+  val data: UShort,
+  val version: UByte,
+)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ff6758462fd83aafbf74fadf0d91daa7da8fee96
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/BoolSerializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object BoolSerializer : Serializer<Boolean> {
+  override fun serialize(buffer: ChainedByteBuffer, data: Boolean) {
+    buffer.put(
+      if (data) 0x01.toByte()
+      else 0x00.toByte()
+    )
+  }
+
+  override fun deserialize(buffer: ByteBuffer): Boolean {
+    return buffer.get() != 0x00.toByte()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d11a523a09cbcb2aa563ca2b01ec7c546d0bc0f6
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ByteSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object ByteSerializer : Serializer<Byte> {
+  override fun serialize(buffer: ChainedByteBuffer, data: Byte) {
+    buffer.put(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer): Byte {
+    return buffer.get()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..871b104c4d4c8825961a7cc4af9358307bf4ff08
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ChainedByteBuffer.kt
@@ -0,0 +1,133 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import de.kuschku.quasseldroid.util.CoroutineChannel
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.yield
+import java.nio.ByteBuffer
+import java.nio.channels.AsynchronousByteChannel
+import java.nio.channels.WritableByteChannel
+import java.util.*
+
+class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: Boolean = false) {
+  private val bufferList: MutableList<ByteBuffer> = ArrayList()
+
+  var size = 0
+    private set
+
+  private var currentBuffer = 0
+
+  private fun allocate(size: Int) = when (direct) {
+    true  -> ByteBuffer.allocateDirect(size)
+    false -> ByteBuffer.allocate(size)
+  }
+
+  private fun ensureSpace(size: Int) {
+    if (bufferList.isEmpty()) {
+      bufferList.add(allocate(bufferSize))
+    }
+    if (bufferList[currentBuffer].remaining() < size) {
+      currentBuffer += 1
+    }
+    if (currentBuffer == bufferList.size) {
+      bufferList.add(allocate(bufferSize))
+    }
+    this.size += size
+  }
+
+  fun put(value: Byte) {
+    ensureSpace(1)
+
+    bufferList.last().put(value)
+  }
+
+  fun putChar(value: Char) {
+    ensureSpace(2)
+
+    bufferList.last().putChar(value)
+  }
+
+  fun putShort(value: Short) {
+    ensureSpace(2)
+
+    bufferList.last().putShort(value)
+  }
+
+  fun putInt(value: Int) {
+    ensureSpace(4)
+
+    bufferList.last().putInt(value)
+  }
+
+  fun putLong(value: Long) {
+    ensureSpace(8)
+
+    bufferList.last().putLong(value)
+  }
+
+  fun putFloat(value: Float) {
+    ensureSpace(4)
+
+    bufferList.last().putFloat(value)
+  }
+
+  fun putDouble(value: Double) {
+    ensureSpace(8)
+
+    bufferList.last().putDouble(value)
+  }
+
+  fun put(value: ByteBuffer) {
+    while (value.remaining() > 8) {
+      putLong(value.long)
+    }
+    while (value.hasRemaining()) {
+      put(value.get())
+    }
+  }
+
+  fun put(value: ByteArray) {
+    value.forEach(this::put)
+  }
+
+  fun clear() {
+    bufferList.clear()
+    currentBuffer = 0
+    size = 0
+  }
+
+  fun buffers() = sequence {
+    for (buffer in bufferList) {
+      buffer.flip()
+      yield(buffer)
+    }
+  }
+
+  fun toBuffer(): ByteBuffer {
+    val byteBuffer = allocate(size)
+    for (buffer in bufferList) {
+      buffer.flip()
+      byteBuffer.put(buffer)
+    }
+    byteBuffer.flip()
+    return byteBuffer
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c414476e5b3eea866ba057e800d5e97bba04540e
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/IntSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object IntSerializer : Serializer<Int> {
+  override fun serialize(buffer: ChainedByteBuffer, data: Int) {
+    buffer.putInt(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer): Int {
+    return buffer.getInt()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..334218595faf6a3a49a383715856300dccf896dc
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/LongSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object LongSerializer : Serializer<Long> {
+  override fun serialize(buffer: ChainedByteBuffer, data: Long) {
+    buffer.putLong(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer): Long {
+    return buffer.getLong()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e4048406798dba29fca52cd45f28f6823df55e9b
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ProtocolInfoSerializer.kt
@@ -0,0 +1,39 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import de.kuschku.quasseldroid.ProtocolInfo
+import java.nio.ByteBuffer
+
+object ProtocolInfoSerializer : Serializer<ProtocolInfo> {
+  override fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo) {
+      UByteSerializer.serialize(buffer, data.flags)
+      UShortSerializer.serialize(buffer, data.data)
+      UByteSerializer.serialize(buffer, data.version)
+  }
+
+  override fun deserialize(buffer: ByteBuffer): ProtocolInfo {
+    return ProtocolInfo(
+            UByteSerializer.deserialize(buffer),
+            UShortSerializer.deserialize(buffer),
+            UByteSerializer.deserialize(buffer)
+    )
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2d6589765231d87943d96d3de980b42bf320b9d2
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/Serializer.kt
@@ -0,0 +1,27 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+interface Serializer<T> {
+  fun serialize(buffer: ChainedByteBuffer, data: T)
+  fun deserialize(buffer: ByteBuffer): T
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a7cd95cb2efb3f89ad315978454160f99b5a77d7
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ShortSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object ShortSerializer : Serializer<Short> {
+  override fun serialize(buffer: ChainedByteBuffer, data: Short) {
+    buffer.putShort(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer): Short {
+    return buffer.getShort()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a288c1ebe4bef01c75e33d77c826fa37373241bd
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UByteSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object UByteSerializer : Serializer<UByte> {
+  override fun serialize(buffer: ChainedByteBuffer, data: UByte) {
+    buffer.put(data.toByte())
+  }
+
+  override fun deserialize(buffer: ByteBuffer): UByte {
+    return buffer.get().toUByte()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..535a6cf3ca800c4fae54d47b4dbf64144ff02d27
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UIntSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object UIntSerializer : Serializer<UInt> {
+  override fun serialize(buffer: ChainedByteBuffer, data: UInt) {
+    buffer.putInt(data.toInt())
+  }
+
+  override fun deserialize(buffer: ByteBuffer): UInt {
+    return buffer.getInt().toUInt()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..188a41874123002527e285a2f6bb79d6035e70a3
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/ULongSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object ULongSerializer : Serializer<ULong> {
+  override fun serialize(buffer: ChainedByteBuffer, data: ULong) {
+    buffer.putLong(data.toLong())
+  }
+
+  override fun deserialize(buffer: ByteBuffer): ULong {
+    return buffer.getLong().toULong()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..da78b5a5e66558ed69c3e85ac5212567e602df53
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/protocol/UShortSerializer.kt
@@ -0,0 +1,32 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.protocol
+
+import java.nio.ByteBuffer
+
+object UShortSerializer : Serializer<UShort> {
+  override fun serialize(buffer: ChainedByteBuffer, data: UShort) {
+    buffer.putShort(data.toShort())
+  }
+
+  override fun deserialize(buffer: ByteBuffer): UShort {
+    return buffer.getShort().toUShort()
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fe3bd2b67a6ec431067a175619971bbb92e66ae9
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/CoroutineChannel.kt
@@ -0,0 +1,52 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util
+
+import de.kuschku.quasseldroid.protocol.ChainedByteBuffer
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.runInterruptible
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.nio.ByteBuffer
+import java.util.concurrent.Executors
+
+class CoroutineChannel {
+  private lateinit var channel: StreamChannel
+  private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
+  private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
+
+  suspend fun connect(address: InetSocketAddress) = runInterruptible(writeContext) {
+    this.channel = StreamChannel(Socket(address.address, address.port))
+  }
+
+  suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) {
+    this.channel.read(buffer)
+  }
+
+  suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
+    this.channel.write(buffer)
+  }
+
+  suspend fun write(chainedBuffer: ChainedByteBuffer) {
+    for (buffer in chainedBuffer.buffers()) {
+      write(buffer)
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6bb7c3127d629519e0fda2ab855f0127dda8155a
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ReadableWrappedChannel.kt
@@ -0,0 +1,90 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util
+
+import android.util.Log
+import java.io.InputStream
+import java.nio.ByteBuffer
+import java.nio.channels.ReadableByteChannel
+import java.nio.channels.spi.AbstractInterruptibleChannel
+
+class ReadableWrappedChannel(
+  private var backingStream: InputStream
+) : AbstractInterruptibleChannel(), ReadableByteChannel {
+  private val buffer = ByteBuffer.allocate(PAGE_SIZE)
+  private val lock = Any()
+
+  override fun read(dst: ByteBuffer): Int {
+    val totalData = dst.remaining()
+    var remainingData = totalData
+
+    // used to mark if we’ve already read any data. This is used to ensure that we only ever block
+    // once for reading
+    var hasRead = false
+
+    synchronized(lock) {
+      // Only read as long as we have content to read, and until we’ve blocked at most once
+      while (remainingData > 0 && !(hasRead && backingStream.available() == 0)) {
+        // Data to be read, always the minimum of available data and the page size
+        val toReadOnce = Math.min(remainingData, PAGE_SIZE)
+        var readData = 0
+
+        try {
+          // begin blocking operation, this handles interruption etc. properly
+          begin()
+          // prepare bufferId for reading by resetting position and limit
+          buffer.clear()
+          // read data into bufferId
+          readData = backingStream.read(buffer.array(), 0, toReadOnce)
+          // accurately set bufferId info
+          buffer.position(readData)
+        } finally {
+          // end blocking operation, this handles interruption etc. properly
+          end(readData > 0)
+        }
+
+        if (readData <= 0) {
+            Log.e("ReadableWrappedChannel", "Read: $readData")
+        }
+
+        // read is negative if no data was read, in that case, terminate
+        if (readData < 0)
+          break
+
+        // add read amount to total
+        remainingData -= readData
+        // mark that we’ve read data (to only block once)
+        hasRead = true
+
+        // flip bufferId to prepare for reading
+        buffer.flip()
+        dst.put(buffer)
+      }
+    }
+
+    return (totalData - remainingData)
+  }
+
+  override fun implCloseChannel() = backingStream.close()
+
+  companion object {
+    private const val PAGE_SIZE = 8192
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c744310e31c2e08bef2162ed3e04a50d88a584e2
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/StreamChannel.kt
@@ -0,0 +1,65 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util
+
+import java.io.Flushable
+import java.io.InputStream
+import java.io.OutputStream
+import java.net.Socket
+import java.nio.ByteBuffer
+import java.nio.channels.ByteChannel
+import java.nio.channels.InterruptibleChannel
+import java.util.zip.DeflaterOutputStream
+import java.util.zip.InflaterInputStream
+
+class StreamChannel constructor(
+  private val socket: Socket,
+  private val inputStream: InputStream = socket.getInputStream(),
+  private val outputStream: OutputStream = socket.getOutputStream(),
+) : Flushable by outputStream, ByteChannel, InterruptibleChannel {
+  private val input = ReadableWrappedChannel(inputStream)
+  private val output = WritableWrappedChannel(outputStream)
+
+  fun withCompression(): StreamChannel {
+    return StreamChannel(
+      socket,
+      InflaterInputStream(inputStream),
+      DeflaterOutputStream(outputStream),
+    )
+  }
+
+  override fun close() {
+    input.close()
+    output.close()
+    socket.close()
+  }
+
+  override fun isOpen(): Boolean {
+    return !socket.isClosed
+  }
+
+  override fun read(dst: ByteBuffer): Int {
+    return input.read(dst)
+  }
+
+  override fun write(src: ByteBuffer): Int {
+    return output.write(src)
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt b/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ef0b3fa3c7437c807519f3ba65f9dc8e3b7cb7d0
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/WritableWrappedChannel.kt
@@ -0,0 +1,71 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util
+
+import java.io.OutputStream
+import java.nio.ByteBuffer
+import java.nio.channels.WritableByteChannel
+import java.nio.channels.spi.AbstractInterruptibleChannel
+
+class WritableWrappedChannel(
+  private var backingStream: OutputStream
+) : AbstractInterruptibleChannel(), WritableByteChannel {
+  private val buffer = ByteBuffer.allocate(PAGE_SIZE)
+  private val lock = Any()
+
+  override fun write(src: ByteBuffer): Int {
+    val totalData = src.remaining()
+    var remainingData = totalData
+
+    synchronized(lock) {
+      while (remainingData > 0) {
+        // Data to be written, always the minimum of available data and the page size
+        val writtenData = Math.min(remainingData, PAGE_SIZE)
+
+        // Set new bufferId info
+        buffer.clear()
+        buffer.limit(writtenData)
+        // Read data into bufferId
+        buffer.put(src)
+
+        try {
+          // begin blocking operation, this handles interruption etc. properly
+          begin()
+          // Write data to backing stream
+          backingStream.write(buffer.array(), 0, writtenData)
+        } finally {
+          // end blocking operation, this handles interruption etc. properly
+          end(writtenData > 0)
+        }
+
+        // add written amount to total
+        remainingData -= writtenData
+      }
+
+      return (totalData - remainingData)
+    }
+  }
+
+  override fun implCloseChannel() = backingStream.close()
+
+  companion object {
+    private const val PAGE_SIZE = 8192
+  }
+}
diff --git a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
index 6e55bcd4a1780d19514b210b4e31025fa5ae10f8..4ebc9b8ae91cd877f5dd8ffa27905f971635334e 100644
--- a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
+++ b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
@@ -1,8 +1,14 @@
 package de.kuschku.quasseldroid
 
+import de.kuschku.quasseldroid.protocol.ChainedByteBuffer
+import de.kuschku.quasseldroid.protocol.IntSerializer
+import de.kuschku.quasseldroid.protocol.UIntSerializer
+import de.kuschku.quasseldroid.util.CoroutineChannel
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
 import org.junit.Test
-
-import org.junit.Assert.*
+import java.net.InetSocketAddress
+import java.nio.ByteBuffer
 
 /**
  * Example local unit test, which will execute on the development machine (host).
@@ -10,8 +16,27 @@ import org.junit.Assert.*
  * See [testing documentation](http://d.android.com/tools/testing).
  */
 class ExampleUnitTest {
-    @Test
-    fun addition_isCorrect() {
-        assertEquals(4, 2 + 2)
+  @Test
+  fun addition_isCorrect() {
+    assertEquals(4, 2 + 2)
+  }
+
+  @Test
+  fun testNetworking() {
+    runBlocking {
+      val sizeBuffer = ByteBuffer.allocateDirect(4)
+      val sendBuffer = ChainedByteBuffer(direct = true)
+      val channel = CoroutineChannel()
+      channel.connect(InetSocketAddress("kuschku.de", 4242))
+      val readBuffer = ByteBuffer.allocateDirect(4)
+      UIntSerializer.serialize(sendBuffer, 0x42b3_3f00u)
+      IntSerializer.serialize(sendBuffer, 2)
+      UIntSerializer.serialize(sendBuffer, 0x8000_0000u)
+      channel.write(sendBuffer)
+      channel.read(readBuffer)
+      readBuffer.flip()
+      println(IntSerializer.deserialize(readBuffer))
     }
-}
\ No newline at end of file
+  }
+}
+