From 17221395dc3037660af1a41d56503b57010f4fff Mon Sep 17 00:00:00 2001 From: "PSp..om" Date: Thu, 5 May 2011 05:07:28 +0000 Subject: [PATCH] The KernelAdapter now keeps a rolling message buffer for the endpoints when using a reliable connection. There is no guarantee that the buffers going out from a client won't be chopped up by the networking stack in various ways. It was always the intent to accumulate them like this (the client side already does after all) but it was an oversight. It's a testament to modern networking that this hasn't come up in practice yet. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7449 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../com/jme3/network/base/KernelAdapter.java | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/engine/src/networking/com/jme3/network/base/KernelAdapter.java b/engine/src/networking/com/jme3/network/base/KernelAdapter.java index 2ef9f3909..e5a3fd132 100644 --- a/engine/src/networking/com/jme3/network/base/KernelAdapter.java +++ b/engine/src/networking/com/jme3/network/base/KernelAdapter.java @@ -34,7 +34,9 @@ package com.jme3.network.base; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -69,6 +71,10 @@ public class KernelAdapter extends Thread private Kernel kernel; private MessageListener messageDispatcher; private AtomicBoolean go = new AtomicBoolean(true); + + // Keeps track of the in-progress messages that are received + // on reliable connections + private Map messageBuffers = new ConcurrentHashMap(); // Marks the messages as reliable or not if they came // through this connector. @@ -107,6 +113,10 @@ public class KernelAdapter extends Thread protected void connectionClosed( Endpoint p ) { + // Remove any message buffer we've been accumulating + // on behalf of this endpoing + messageBuffers.remove(p); + server.connectionClosed(p); } @@ -156,16 +166,49 @@ public class KernelAdapter extends Thread } } + protected MessageProtocol getMessageBuffer( Endpoint p ) + { + if( !reliable ) { + // Since UDP comes in packets and they aren't split + // up, there is no reason to buffer. In fact, there would + // be a down side because there is no way for us to reliably + // clean these up later since we'd create another one for + // any random UDP packet that comes to the port. + return new MessageProtocol(); + } else { + // See if we already have one + MessageProtocol result = messageBuffers.get(p); + if( result != null ) { + result = new MessageProtocol(); + messageBuffers.put(p, result); + } + return result; + } + } + protected void createAndDispatch( Envelope env ) { - MessageProtocol protocol = new MessageProtocol(); + MessageProtocol protocol = getMessageBuffer(env.getSource()); byte[] data = env.getData(); ByteBuffer buffer = ByteBuffer.wrap(data); int count = protocol.addBuffer( buffer ); - if( count == 0 ) - throw new RuntimeException( "Envelope contained incomplete data:" + env ); + if( count == 0 ) { + // This can happen if there was only a partial message + // received. However, this should never happen for unreliable + // connections. + if( !reliable ) { + // Log some additional information about the packet. + int len = Math.min( 10, data.length ); + StringBuilder sb = new StringBuilder(); + for( int i = 0; i < len; i++ ) { + sb.append( "[" + Integer.toHexString(data[i]) + "]" ); + } + log.log( Level.INFO, "First 10 bytes of incomplete nessage:" + sb ); + throw new RuntimeException( "Envelope contained incomplete data:" + env ); + } + } // Should be complete... and maybe we should check but we don't Message m = null;