🐛 Fix connectivity issues and improve code

This commit is contained in:
2018-10-15 10:49:04 +02:00
parent fe9fa0ba2f
commit 519f457476
13 changed files with 152 additions and 313 deletions

View File

@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory
import java.io.IOException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
/**
* Contains everything used by both the old streams-oriented NetworkHandler and the new NioNetworkHandler,
@ -165,11 +166,12 @@ class Connection(
}
}
// the TCP timeout starts out at 20 seconds
// According to the specification, the TCP timeout starts out at 20 seconds
// after verack messages are exchanged, the timeout is raised to 10 minutes
// Let's tweak these numbers a bit:
fun isExpired(): Boolean = when (state) {
State.CONNECTING -> io.lastUpdate < System.currentTimeMillis() - 20000
State.ACTIVE -> io.lastUpdate < System.currentTimeMillis() - 600000
State.CONNECTING -> io.lastUpdate < System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(9)
State.ACTIVE -> io.lastUpdate < System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(3)
State.DISCONNECTED -> true
}

View File

@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.GetData
import ch.dissem.bitmessage.entity.MessagePayload
import ch.dissem.bitmessage.entity.NetworkMessage
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
import ch.dissem.bitmessage.exception.NodeException
import ch.dissem.bitmessage.factory.V3MessageReader
import ch.dissem.bitmessage.utils.UnixTime
import org.slf4j.LoggerFactory
@ -42,7 +41,7 @@ class ConnectionIO(
) {
private val headerOut: ByteBuffer = ByteBuffer.allocate(HEADER_SIZE)
private var payloadOut: ByteBuffer? = null
private var reader: V3MessageReader? = V3MessageReader()
private val reader = V3MessageReader()
internal val sendingQueue: Deque<MessagePayload> = ConcurrentLinkedDeque<MessagePayload>()
internal var lastUpdate = System.currentTimeMillis()
@ -55,8 +54,7 @@ class ConnectionIO(
headerOut.flip()
}
val inBuffer: ByteBuffer
get() = reader?.getActiveBuffer() ?: throw NodeException("Node is disconnected")
val inBuffer: ByteBuffer = reader.buffer
fun updateWriter() {
if (!headerOut.hasRemaining() && !sendingQueue.isEmpty()) {
@ -78,7 +76,7 @@ class ConnectionIO(
}
fun updateReader() {
reader?.let { reader ->
reader.let { reader ->
reader.update()
if (!reader.getMessages().isEmpty()) {
val iterator = reader.getMessages().iterator()
@ -96,11 +94,11 @@ class ConnectionIO(
fun updateSyncStatus() {
if (!isSyncFinished) {
isSyncFinished = reader?.getMessages()?.isEmpty() ?: true && syncFinished(null)
isSyncFinished = reader.getMessages().isEmpty() && syncFinished(null)
}
}
protected fun syncFinished(msg: NetworkMessage?): Boolean {
private fun syncFinished(msg: NetworkMessage?): Boolean {
if (mode != Connection.Mode.SYNC) {
return false
}
@ -127,10 +125,6 @@ class ConnectionIO(
}
fun disconnect() {
reader?.let {
it.cleanup()
reader = null
}
payloadOut = null
}
@ -151,7 +145,7 @@ class ConnectionIO(
|| headerOut.hasRemaining()
|| payloadOut?.hasRemaining() ?: false
fun nothingToSend() = sendingQueue.isEmpty()
private fun nothingToSend() = sendingQueue.isEmpty()
companion object {
val LOG = LoggerFactory.getLogger(ConnectionIO::class.java)

View File

@ -77,12 +77,12 @@ class NetworkConnectionInitializer(
activateConnection()
}
} else {
throw NodeException("Received unsupported version " + version.version + ", disconnecting.")
throw NodeException("Received unsupported version ${version.version}, disconnecting.")
}
}
private fun activateConnection() {
LOG.info("Successfully established connection with node " + node)
LOG.info("Successfully established connection with node $node")
markActive(version.streams)
node.time = UnixTime.now
if (mode != Connection.Mode.SYNC) {

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.networking.nio
import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.constants.Network.HEADER_SIZE
import ch.dissem.bitmessage.constants.Network.NETWORK_MAGIC_NUMBER
import ch.dissem.bitmessage.entity.CustomMessage
import ch.dissem.bitmessage.entity.GetData
@ -24,7 +25,6 @@ import ch.dissem.bitmessage.entity.NetworkMessage
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
import ch.dissem.bitmessage.exception.NodeException
import ch.dissem.bitmessage.factory.BufferPool
import ch.dissem.bitmessage.factory.V3MessageReader
import ch.dissem.bitmessage.networking.nio.Connection.Mode.*
import ch.dissem.bitmessage.ports.NetworkHandler
@ -94,29 +94,26 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
override fun send(server: InetAddress, port: Int, request: CustomMessage): CustomMessage {
SocketChannel.open(InetSocketAddress(server, port)).use { channel ->
channel.configureBlocking(true)
val headerBuffer = BufferPool.allocateHeaderBuffer()
val headerBuffer = ByteBuffer.allocate(HEADER_SIZE)
val payloadBuffer = NetworkMessage(request).writer().writeHeaderAndGetPayloadBuffer(headerBuffer)
headerBuffer.flip()
while (headerBuffer.hasRemaining()) {
channel.write(headerBuffer)
}
BufferPool.deallocate(headerBuffer)
while (payloadBuffer.hasRemaining()) {
channel.write(payloadBuffer)
}
val reader = V3MessageReader()
while (channel.isConnected && reader.getMessages().isEmpty()) {
if (channel.read(reader.getActiveBuffer()) > 0) {
if (channel.read(reader.buffer) > 0) {
reader.update()
} else {
reader.cleanup()
throw NodeException("No response from node $server")
}
}
val networkMessage: NetworkMessage?
if (reader.getMessages().isEmpty()) {
reader.cleanup()
throw NodeException("No response from node $server")
} else {
networkMessage = reader.getMessages().first()
@ -125,7 +122,6 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
if (networkMessage.payload is CustomMessage) {
return networkMessage.payload as CustomMessage
} else {
reader.cleanup()
throw NodeException("Unexpected response from node $server: ${networkMessage.payload.javaClass}")
}
}
@ -196,14 +192,14 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
request(delayed)
try {
Thread.sleep(30000)
Thread.sleep(10000)
} catch (e: InterruptedException) {
return@thread
}
}
}
thread("selector worker", {
thread("selector worker") {
try {
val serverChannel = ServerSocketChannel.open()
this.serverChannel = serverChannel
@ -272,10 +268,13 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
else -> key.interestOps(OP_READ)
}
} catch (e: CancelledKeyException) {
LOG.debug("${e.message}: ${connection.node}", e)
connection.disconnect()
} catch (e: NodeException) {
LOG.debug("${e.message}: ${connection.node}", e)
connection.disconnect()
} catch (e: IOException) {
LOG.debug("${e.message}: ${connection.node}", e)
connection.disconnect()
}
}
@ -306,10 +305,11 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
channel.configureBlocking(false)
channel.connect(InetSocketAddress(address.toInetAddress(), address.port))
val connection = Connection(ctx, CLIENT, address, requestedObjects, 0)
connections.put(
connection,
channel.register(selector, OP_CONNECT, connection)
)
connections[connection] = channel.register(selector, OP_CONNECT, connection)
LOG.debug("Connection registered to $address")
} catch (ignore: NoRouteToHostException) {
// We'll try to connect to many offline nodes, so
// this is expected to happen quite a lot.
@ -321,7 +321,11 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
LOG.error(e.message, e)
}
} catch (e: IOException) {
LOG.error(e.message, e)
if (e.message == "Network is unreachable") {
LOG.debug("Network is unreachable: $address")
} else {
LOG.error("${e.message}: $address", e)
}
}
}
@ -335,7 +339,7 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
// isn't nice though.
LOG.error(e.message, e)
}
})
}
}
private fun thread(threadName: String, runnable: () -> Unit): Thread {
@ -380,7 +384,7 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
val distribution = HashMap<Connection, MutableList<InventoryVector>>()
for ((connection, _) in connections) {
if (connection.state == Connection.State.ACTIVE) {
distribution.put(connection, mutableListOf<InventoryVector>())
distribution[connection] = mutableListOf()
}
}
if (distribution.isEmpty()) {
@ -455,7 +459,7 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
val outgoing = outgoingConnections[stream] ?: 0
streamProperties.add(
Property(
"stream " + stream, Property("nodes", incoming + outgoing),
"stream $stream", Property("nodes", incoming + outgoing),
Property("incoming", incoming),
Property("outgoing", outgoing)
)
@ -476,8 +480,8 @@ class NioNetworkHandler(private val magicNetworkNumber: Int = NETWORK_MAGIC_NUMB
companion object {
private val LOG = LoggerFactory.getLogger(NioNetworkHandler::class.java)
private val REQUESTED_OBJECTS_MAX_TIME = (2 * 60000).toLong() // 2 minutes in ms
private val DELAYED = java.lang.Long.MIN_VALUE
private const val REQUESTED_OBJECTS_MAX_TIME = 2 * 60000L // 2 minutes in ms
private const val DELAYED = java.lang.Long.MIN_VALUE
private fun write(channel: SocketChannel, connection: ConnectionIO) {
writeBuffer(connection.outBuffers, channel)