the SM module that simply routes raw bytes over transports, hiding the particular transport implementation and threading model. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7009 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
d9f39f6b0b
commit
f18fb0b287
@ -0,0 +1,123 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
import java.util.concurrent.LinkedBlockingQueue; |
||||
import java.util.concurrent.atomic.AtomicLong; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* Base implementation of the Kernel interface providing several |
||||
* useful default implementations of some methods. This implementation |
||||
* assumes that the kernel will be managing its own internal threads |
||||
* and queuing any results for the caller to retrieve on their own |
||||
* thread. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public abstract class AbstractKernel implements Kernel |
||||
{ |
||||
static Logger log = Logger.getLogger(AbstractKernel.class.getName()); |
||||
|
||||
private AtomicLong nextId = new AtomicLong(1); |
||||
|
||||
/** |
||||
* Contains the pending endpoint events waiting for the caller |
||||
* to retrieve them. |
||||
*/ |
||||
private ConcurrentLinkedQueue<EndpointEvent> endpointEvents = new ConcurrentLinkedQueue<EndpointEvent>(); |
||||
|
||||
/** |
||||
* Contains the pending envelopes waiting for the caller to |
||||
* retrieve them. |
||||
*/ |
||||
private LinkedBlockingQueue<Envelope> envelopes = new LinkedBlockingQueue<Envelope>(); |
||||
|
||||
protected AbstractKernel() |
||||
{ |
||||
} |
||||
|
||||
protected void reportError( Exception e ) |
||||
{ |
||||
// Should really be queued up so the outer thread can
|
||||
// retrieve them. For now we'll just log it. FIXME
|
||||
log.log( Level.SEVERE, "Unhanddled kernel error", e ); |
||||
} |
||||
|
||||
protected long nextEndpointId() |
||||
{ |
||||
return nextId.getAndIncrement(); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if there are waiting envelopes. |
||||
*/ |
||||
public boolean hasEnvelopes() |
||||
{ |
||||
return !envelopes.isEmpty(); |
||||
} |
||||
|
||||
/** |
||||
* Removes one envelope from the received messages queue or |
||||
* blocks until one is available. |
||||
*/ |
||||
public Envelope read() throws InterruptedException |
||||
{ |
||||
return envelopes.take(); |
||||
} |
||||
|
||||
/** |
||||
* Removes and returnsn one endpoint event from the event queue or |
||||
* null if there are no endpoint events. |
||||
*/ |
||||
public EndpointEvent nextEvent() |
||||
{ |
||||
return endpointEvents.poll(); |
||||
} |
||||
|
||||
protected void addEvent( EndpointEvent e ) |
||||
{ |
||||
endpointEvents.add( e ); |
||||
} |
||||
|
||||
protected void addEnvelope( Envelope env ) |
||||
{ |
||||
if( !envelopes.offer( env ) ) { |
||||
throw new KernelException( "Critical error, could not enqueue envelope." ); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,82 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/** |
||||
* A single channel remote connection allowing the sending |
||||
* and receiving of data. As opposed to the Kernel, this will |
||||
* only ever receive data from one Endpoint and so bypasses |
||||
* the envelope wrapping. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface Connector |
||||
{ |
||||
/** |
||||
* Returns true if this connector is currently connected. |
||||
*/ |
||||
public boolean isConnected(); |
||||
|
||||
/** |
||||
* Closes the connection. Any subsequent attempts to read |
||||
* or write will fail with an exception. |
||||
*/ |
||||
public void close(); |
||||
|
||||
/** |
||||
* Returns true if there is currently data available for |
||||
* reading. Some connector implementations may not be able |
||||
* to answer this question accurately and will always return |
||||
* false. |
||||
*/ |
||||
public boolean available(); |
||||
|
||||
/** |
||||
* Reads a chunk of data from the connection, blocking if |
||||
* there is no data available. The buffer may only be valid |
||||
* until the next read() call is made. Callers should copy |
||||
* the data if they need it for longer than that. |
||||
* |
||||
* @return The data read or null if there is no more data |
||||
* because the connection is closed. |
||||
*/ |
||||
public ByteBuffer read(); |
||||
|
||||
/** |
||||
* Writes a chunk of data to the connection. |
||||
*/ |
||||
public void write( ByteBuffer data ); |
||||
} |
@ -0,0 +1,54 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
|
||||
/** |
||||
* Represents a client-side connection error, usually encapsulating |
||||
* an IOException as its cause. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class ConnectorException extends RuntimeException |
||||
{ |
||||
public ConnectorException( String message, Throwable cause ) |
||||
{ |
||||
super( message, cause ); |
||||
} |
||||
|
||||
public ConnectorException( String message ) |
||||
{ |
||||
super( message ); |
||||
} |
||||
} |
@ -0,0 +1,72 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/** |
||||
* An abstract endpoint in a Kernel that can be used for |
||||
* sending/receiving messages within the kernel space. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface Endpoint |
||||
{ |
||||
/** |
||||
* Returns an ID that is unique for this endpoint within its |
||||
* Kernel instance. |
||||
*/ |
||||
public long getId(); |
||||
|
||||
/** |
||||
* Returns the kernel to which this endpoint belongs. |
||||
*/ |
||||
public Kernel getKernel(); |
||||
|
||||
/** |
||||
* Returns true if this endpoint is currently connected. |
||||
*/ |
||||
public boolean isConnected(); |
||||
|
||||
/** |
||||
* Sends data to the other end of the connection represented |
||||
* by this endpoint. |
||||
*/ |
||||
public void send( ByteBuffer data ); |
||||
|
||||
/** |
||||
* Closes this endpoint. |
||||
*/ |
||||
public void close(); |
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
|
||||
/** |
||||
* Provides information about an added or |
||||
* removed connection. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class EndpointEvent |
||||
{ |
||||
public enum Type { ADD, REMOVE }; |
||||
|
||||
private Kernel source; |
||||
private Endpoint endpoint; |
||||
private Type type; |
||||
|
||||
public EndpointEvent( Kernel source, Endpoint p, Type type ) |
||||
{ |
||||
this.source = source; |
||||
this.endpoint = p; |
||||
this.type = type; |
||||
} |
||||
|
||||
public static EndpointEvent createAdd( Kernel source, Endpoint p ) |
||||
{ |
||||
return new EndpointEvent( source, p, Type.ADD ); |
||||
} |
||||
|
||||
public static EndpointEvent createRemove( Kernel source, Endpoint p ) |
||||
{ |
||||
return new EndpointEvent( source, p, Type.REMOVE ); |
||||
} |
||||
|
||||
public Kernel getSource() |
||||
{ |
||||
return source; |
||||
} |
||||
|
||||
public Endpoint getEndpoint() |
||||
{ |
||||
return endpoint; |
||||
} |
||||
|
||||
public Type getType() |
||||
{ |
||||
return type; |
||||
} |
||||
|
||||
public String toString() |
||||
{ |
||||
return "EndpointEvent[" + type + ", " + endpoint + "]"; |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/** |
||||
* Encapsulates a received piece of data. This is used by the Kernel |
||||
* to track incoming chunks of data. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class Envelope |
||||
{ |
||||
private Endpoint source; |
||||
private byte[] data; |
||||
private boolean reliable; |
||||
|
||||
/** |
||||
* Creates an incoming envelope holding the data from the specified |
||||
* source. The 'reliable' flag further indicates on which mode of |
||||
* transport the data arrrived. |
||||
*/ |
||||
public Envelope( Endpoint source, byte[] data, boolean reliable ) |
||||
{ |
||||
this.source = source; |
||||
this.data = data; |
||||
this.reliable = reliable; |
||||
} |
||||
|
||||
public Endpoint getSource() |
||||
{ |
||||
return source; |
||||
} |
||||
|
||||
public byte[] getData() |
||||
{ |
||||
return data; |
||||
} |
||||
|
||||
public boolean isReliable() |
||||
{ |
||||
return reliable; |
||||
} |
||||
|
||||
public String toString() |
||||
{ |
||||
return "Envelope[" + source + ", " + (reliable?"reliable":"unreliable") + ", " + data.length + "]"; |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Collection; |
||||
|
||||
/** |
||||
* Defines the basic byte[] passing messaging |
||||
* kernel. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface Kernel |
||||
{ |
||||
/** |
||||
* Initializes the kernel and starts any internal processing. |
||||
*/ |
||||
public void initialize(); |
||||
|
||||
/** |
||||
* Gracefully terminates the kernel and stops any internal |
||||
* daemon processing. This method will not return until all |
||||
* internal threads have been shut down. |
||||
*/ |
||||
public void terminate() throws InterruptedException; |
||||
|
||||
/** |
||||
* Dispatches the data to all endpoints managed by the |
||||
* kernel. 'routing' is currently ignored. |
||||
*/ |
||||
public void broadcast( Object routing, ByteBuffer data, boolean reliable ); |
||||
|
||||
/** |
||||
* Returns true if there are waiting envelopes. |
||||
*/ |
||||
public boolean hasEnvelopes(); |
||||
|
||||
/** |
||||
* Removes one envelope from the received messages queue or |
||||
* blocks until one is available. |
||||
*/ |
||||
public Envelope read() throws InterruptedException; |
||||
|
||||
/** |
||||
* Removes and returnsn one endpoint event from the event queue or |
||||
* null if there are no endpoint events. |
||||
*/ |
||||
public EndpointEvent nextEvent(); |
||||
} |
@ -0,0 +1,54 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel; |
||||
|
||||
|
||||
/** |
||||
* Represents a kernel-level error, usually encapsulating |
||||
* an IOException as its cause. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class KernelException extends RuntimeException |
||||
{ |
||||
public KernelException( String message, Throwable cause ) |
||||
{ |
||||
super( message, cause ); |
||||
} |
||||
|
||||
public KernelException( String message ) |
||||
{ |
||||
super( message ); |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
||||
<html> |
||||
<body> |
||||
The kernel package is the heart of the JME networking module |
||||
and controls the routing and dispatch of message data over |
||||
different transport implementations. Most users will never |
||||
have to deal with these classes unless they are writing their own |
||||
client and server implementations that diverge from the standard |
||||
classes that are provided. |
||||
|
||||
<p>{@link Kernel} defines the core of a server-side message |
||||
broker that abstracts away the specific transport and underlying |
||||
threading model used. For example, it might use NIO selectors |
||||
in a single threaded model or straight multithreaded socket |
||||
model. Or it might implement SSL connections. Once created, |
||||
{@link Kernel} users don't need to care about the details.</p> |
||||
|
||||
<p>{@link Endpoint} is a managed connection within a {@link Kernel} |
||||
providing kernel to client connectivity.</p> |
||||
|
||||
<p>{@link Connector} defines the basic client-side message sender |
||||
and these objects are typically used to connect to a {@link Kernel} though |
||||
they can connect to any network port that supports the implementation's |
||||
protocol. Implementations are provided for straight TCP and UDP communication |
||||
and could be extended to support SSL or different threading models.</p> |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,148 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.tcp; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.SocketChannel; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
|
||||
/** |
||||
* Endpoint implementation that encapsulates the |
||||
* channel IO based connection information and keeps |
||||
* track of the outbound data queue for the channel. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class NioEndpoint implements Endpoint |
||||
{ |
||||
private long id; |
||||
private SocketChannel socket; |
||||
private SelectorKernel kernel; |
||||
private ConcurrentLinkedQueue<ByteBuffer> outbound = new ConcurrentLinkedQueue<ByteBuffer>(); |
||||
|
||||
public NioEndpoint( SelectorKernel kernel, long id, SocketChannel socket ) |
||||
{ |
||||
this.id = id; |
||||
this.socket = socket; |
||||
this.kernel = kernel; |
||||
} |
||||
|
||||
public Kernel getKernel() |
||||
{ |
||||
return kernel; |
||||
} |
||||
|
||||
public void close() |
||||
{ |
||||
try { |
||||
kernel.closeEndpoint(this); |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error closing endpoint for socket:" + socket, e ); |
||||
} |
||||
} |
||||
|
||||
public long getId() |
||||
{ |
||||
return id; |
||||
} |
||||
|
||||
public boolean isConnected() |
||||
{ |
||||
return socket.isConnected(); |
||||
} |
||||
|
||||
/** |
||||
* The wakeup option is used internally when the kernel is |
||||
* broadcasting out to a bunch of endpoints and doesn't want to |
||||
* necessarily wakeup right away. |
||||
*/ |
||||
protected void send( ByteBuffer data, boolean copy, boolean wakeup ) |
||||
{ |
||||
// We create a ByteBuffer per endpoint since we
|
||||
// use it to track the data sent to each endpoint
|
||||
// separately.
|
||||
ByteBuffer buffer; |
||||
if( !copy ) { |
||||
buffer = data; |
||||
} else { |
||||
// Copy the buffer
|
||||
buffer = ByteBuffer.allocate(data.remaining()); |
||||
buffer.put(data); |
||||
buffer.flip(); |
||||
} |
||||
|
||||
// Queue it up
|
||||
outbound.add(buffer); |
||||
|
||||
if( wakeup ) |
||||
kernel.wakeupSelector(); |
||||
} |
||||
|
||||
/** |
||||
* Called by the SelectorKernel to get the current top |
||||
* buffer for writing. |
||||
*/ |
||||
protected ByteBuffer peekPending() |
||||
{ |
||||
return outbound.peek(); |
||||
} |
||||
|
||||
/** |
||||
* Called by the SelectorKernel when the top buffer |
||||
* has been exhausted. |
||||
*/ |
||||
protected ByteBuffer removePending() |
||||
{ |
||||
return outbound.poll(); |
||||
} |
||||
|
||||
protected boolean hasPending() |
||||
{ |
||||
return !outbound.isEmpty(); |
||||
} |
||||
|
||||
public void send( ByteBuffer data ) |
||||
{ |
||||
send( data, true, true ); |
||||
} |
||||
|
||||
public String toString() |
||||
{ |
||||
return "NioEndpoint[" + id + ", " + socket + "]"; |
||||
} |
||||
} |
@ -0,0 +1,402 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.tcp; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.*; |
||||
import java.nio.channels.spi.SelectorProvider; |
||||
import java.net.InetAddress; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.Socket; |
||||
import java.util.*; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
|
||||
/** |
||||
* A Kernel implementation based on NIO selectors. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class SelectorKernel extends AbstractKernel |
||||
{ |
||||
private InetAddress host; |
||||
private int port; |
||||
private SelectorThread thread; |
||||
|
||||
private Map<Long,NioEndpoint> endpoints = new ConcurrentHashMap<Long,NioEndpoint>(); |
||||
|
||||
public SelectorKernel( InetAddress host, int port ) |
||||
{ |
||||
this.host = host; |
||||
this.port = port; |
||||
} |
||||
|
||||
public SelectorKernel( int port ) throws IOException |
||||
{ |
||||
this( InetAddress.getLocalHost(), port ); |
||||
} |
||||
|
||||
protected SelectorThread createSelectorThread() |
||||
{ |
||||
return new SelectorThread(); |
||||
} |
||||
|
||||
public void initialize() |
||||
{ |
||||
if( thread != null ) |
||||
throw new IllegalStateException( "Kernel already initialized." ); |
||||
|
||||
thread = createSelectorThread(); |
||||
|
||||
try { |
||||
thread.connect(); |
||||
thread.start(); |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error hosting:" + host + " port:" + port, e ); |
||||
} |
||||
} |
||||
|
||||
public void terminate() throws InterruptedException |
||||
{ |
||||
if( thread == null ) |
||||
throw new IllegalStateException( "Kernel not initialized." ); |
||||
|
||||
try { |
||||
thread.close(); |
||||
thread = null; |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error closing host connection:" + host + " port:" + port, e ); |
||||
} |
||||
} |
||||
|
||||
public void broadcast( Object routing, ByteBuffer data, boolean reliable ) |
||||
{ |
||||
if( !reliable ) |
||||
throw new UnsupportedOperationException( "Unreliable send not supported by this kernel." ); |
||||
|
||||
// Copy the data just once
|
||||
byte[] copy = new byte[data.remaining()]; |
||||
System.arraycopy(data.array(), data.position(), copy, 0, data.remaining()); |
||||
|
||||
// Hand it to all of the endpoints that match our routing
|
||||
for( NioEndpoint p : endpoints.values() ) { |
||||
// Does it match the routing information? FIXME
|
||||
|
||||
// Give it the data
|
||||
p.send( data, false, false ); |
||||
} |
||||
|
||||
// Wake up the selector so it can reinitialize its
|
||||
// state accordingly.
|
||||
wakeupSelector(); |
||||
} |
||||
|
||||
protected NioEndpoint addEndpoint( SocketChannel c ) |
||||
{ |
||||
// Note: we purposely do NOT put the key in the endpoint.
|
||||
// SelectionKeys are dangerous outside the selector thread
|
||||
// and this is safer.
|
||||
NioEndpoint p = new NioEndpoint( this, nextEndpointId(), c ); |
||||
|
||||
endpoints.put( p.getId(), p ); |
||||
|
||||
// Enqueue an endpoint event for the listeners
|
||||
addEvent( EndpointEvent.createAdd( this, p ) ); |
||||
|
||||
return p; |
||||
} |
||||
|
||||
protected void removeEndpoint( NioEndpoint p, SocketChannel c ) |
||||
{ |
||||
endpoints.remove( p.getId() ); |
||||
|
||||
// Enqueue an endpoint event for the listeners
|
||||
addEvent( EndpointEvent.createRemove( this, p ) ); |
||||
} |
||||
|
||||
/** |
||||
* Called by the endpoints when they need to be closed. |
||||
*/ |
||||
protected void closeEndpoint( NioEndpoint p ) throws IOException |
||||
{ |
||||
thread.cancel(p); |
||||
} |
||||
|
||||
/** |
||||
* Used internally by the endpoints to wakeup the selector |
||||
* when they have data to send. |
||||
*/ |
||||
protected void wakeupSelector() |
||||
{ |
||||
thread.wakeupSelector(); |
||||
} |
||||
|
||||
protected void newData( NioEndpoint p, SocketChannel c, ByteBuffer shared, int size ) |
||||
{ |
||||
// Note: if ever desirable, it would be possible to accumulate
|
||||
// data per source channel and only 'finalize' it when
|
||||
// asked for more envelopes then were ready. I just don't
|
||||
// think it will be an issue in practice. The busier the
|
||||
// server, the more the buffers will fill before we get to them.
|
||||
// And if the server isn't busy, who cares if we chop things up
|
||||
// smaller... the network is still likely to deliver things in
|
||||
// bulk anyway.
|
||||
|
||||
// Must copy the shared data before we use it
|
||||
byte[] dataCopy = new byte[size]; |
||||
System.arraycopy(shared.array(), 0, dataCopy, 0, size); |
||||
|
||||
Envelope env = new Envelope( p, dataCopy, true ); |
||||
addEnvelope( env ); |
||||
} |
||||
|
||||
/** |
||||
* This class is purposely tucked neatly away because |
||||
* messing with the selector from other threads for any |
||||
* reason is very bad. This is the safest architecture. |
||||
*/ |
||||
protected class SelectorThread extends Thread |
||||
{ |
||||
private ServerSocketChannel serverChannel; |
||||
private Selector selector; |
||||
private AtomicBoolean go = new AtomicBoolean(true); |
||||
private ByteBuffer working = ByteBuffer.allocate( 8192 ); |
||||
|
||||
/** |
||||
* Because we want to keep the keys to ourselves, we'll do |
||||
* the endpoint -> key mapping internally. |
||||
*/ |
||||
private Map<NioEndpoint,SelectionKey> endpointKeys = new ConcurrentHashMap<NioEndpoint,SelectionKey>(); |
||||
|
||||
public SelectorThread() |
||||
{ |
||||
setName( "Selector@" + host + ":" + port ); |
||||
setDaemon(true); |
||||
} |
||||
|
||||
public void connect() throws IOException |
||||
{ |
||||
// Create a new selector
|
||||
this.selector = SelectorProvider.provider().openSelector(); |
||||
|
||||
// Create a new non-blocking server socket channel
|
||||
this.serverChannel = ServerSocketChannel.open(); |
||||
serverChannel.configureBlocking(false); |
||||
|
||||
// Bind the server socket to the specified address and port
|
||||
InetSocketAddress isa = new InetSocketAddress(host, port); |
||||
serverChannel.socket().bind(isa); |
||||
|
||||
// Register the server socket channel, indicating an interest in
|
||||
// accepting new connections
|
||||
serverChannel.register(selector, SelectionKey.OP_ACCEPT); |
||||
} |
||||
|
||||
public void close() throws IOException, InterruptedException |
||||
{ |
||||
// Set the thread to stop
|
||||
go.set(false); |
||||
|
||||
// Make sure the channel is closed
|
||||
serverChannel.close(); |
||||
|
||||
// And wait for it
|
||||
join(); |
||||
} |
||||
|
||||
protected void wakeupSelector() |
||||
{ |
||||
selector.wakeup(); |
||||
} |
||||
|
||||
protected void setupSelectorOptions() |
||||
{ |
||||
// For now, selection keys will either be in OP_READ
|
||||
// or OP_WRITE. So while we are writing a buffer, we
|
||||
// will not be reading. This is way simpler and less
|
||||
// error prone... it can always be changed when everything
|
||||
// else works if we are looking to micro-optimize.
|
||||
|
||||
// Setup options based on the current state of
|
||||
// the endpoints. This could potentially be more
|
||||
// efficiently done as change requests... or simply
|
||||
// keeping a thread-safe set of endpoints with pending
|
||||
// writes. For most cases, it shouldn't matter.
|
||||
for( Map.Entry<NioEndpoint,SelectionKey> e : endpointKeys.entrySet() ) { |
||||
if( e.getKey().hasPending() ) |
||||
e.getValue().interestOps(SelectionKey.OP_WRITE); |
||||
} |
||||
} |
||||
|
||||
protected void accept( SelectionKey key ) throws IOException |
||||
{ |
||||
// Would only get accepts on a server channel
|
||||
ServerSocketChannel serverChan = (ServerSocketChannel)key.channel(); |
||||
|
||||
// Setup the connection to be non-blocking
|
||||
SocketChannel remoteChan = serverChan.accept(); |
||||
remoteChan.configureBlocking(false); |
||||
|
||||
// And disable Nagle's buffering algorithm... we want
|
||||
// data to go when we put it there.
|
||||
Socket sock = remoteChan.socket(); |
||||
sock.setTcpNoDelay(true); |
||||
|
||||
// Let the selector know we're interested in reading
|
||||
// data from the channel
|
||||
SelectionKey endKey = remoteChan.register( selector, SelectionKey.OP_READ ); |
||||
|
||||
// And now create a new endpoint
|
||||
NioEndpoint p = addEndpoint( remoteChan ); |
||||
endKey.attach(p); |
||||
endpointKeys.put(p, endKey); |
||||
} |
||||
|
||||
protected void cancel( NioEndpoint p ) throws IOException |
||||
{ |
||||
SelectionKey key = endpointKeys.remove(p); |
||||
SocketChannel c = (SocketChannel)key.channel(); |
||||
|
||||
// Note: key.cancel() is specifically thread safe. One of
|
||||
// the few things one can do with a key from another
|
||||
// thread.
|
||||
key.cancel(); |
||||
c.close(); |
||||
removeEndpoint( p, c ); |
||||
} |
||||
|
||||
protected void cancel( SelectionKey key, SocketChannel c ) throws IOException |
||||
{ |
||||
NioEndpoint p = (NioEndpoint)key.attachment(); |
||||
endpointKeys.remove(p); |
||||
|
||||
key.cancel(); |
||||
c.close(); |
||||
removeEndpoint( p, c ); |
||||
} |
||||
|
||||
protected void read( SelectionKey key ) throws IOException |
||||
{ |
||||
NioEndpoint p = (NioEndpoint)key.attachment(); |
||||
SocketChannel c = (SocketChannel)key.channel(); |
||||
working.clear(); |
||||
|
||||
int size; |
||||
try { |
||||
size = c.read(working); |
||||
} catch( IOException e ) { |
||||
// The remove end forcibly closed the connection...
|
||||
// close out our end and cancel the key
|
||||
cancel( key, c ); |
||||
return; |
||||
} |
||||
|
||||
if( size == -1 ) { |
||||
// The remote end shut down cleanly...
|
||||
// close out our end and cancel the key
|
||||
cancel( key, c ); |
||||
return; |
||||
} |
||||
|
||||
newData( p, c, working, size ); |
||||
} |
||||
|
||||
protected void write( SelectionKey key ) throws IOException |
||||
{ |
||||
NioEndpoint p = (NioEndpoint)key.attachment(); |
||||
SocketChannel c = (SocketChannel)key.channel(); |
||||
|
||||
// We will send what we can and move on.
|
||||
ByteBuffer current = p.peekPending(); |
||||
c.write( current ); |
||||
|
||||
// If we wrote all of that packet then we need to remove it
|
||||
if( current.remaining() == 0 ) { |
||||
p.removePending(); |
||||
} |
||||
|
||||
// If we happened to empty the pending queue then let's read
|
||||
// again.
|
||||
if( !p.hasPending() ) { |
||||
key.interestOps( SelectionKey.OP_READ ); |
||||
} |
||||
} |
||||
|
||||
protected void select() throws IOException |
||||
{ |
||||
selector.select(); |
||||
|
||||
for( Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext(); ) { |
||||
SelectionKey key = i.next(); |
||||
i.remove(); |
||||
|
||||
if( !key.isValid() ) |
||||
continue; |
||||
|
||||
if( key.isAcceptable() ) |
||||
accept(key); |
||||
else if( key.isWritable() ) |
||||
write(key); |
||||
else if( key.isReadable() ) |
||||
read(key); |
||||
} |
||||
} |
||||
|
||||
public void run() |
||||
{ |
||||
// An atomic is safest and costs almost nothing
|
||||
while( go.get() ) { |
||||
// Setup any queued option changes
|
||||
setupSelectorOptions(); |
||||
|
||||
// Check for available keys and process them
|
||||
try { |
||||
select(); |
||||
} catch( ClosedSelectorException e ) { |
||||
if( !go.get() ) |
||||
return; // it's because we're shutting down
|
||||
throw new KernelException( "Premature selector closing", e ); |
||||
} catch( IOException e ) { |
||||
if( !go.get() ) |
||||
return; // error likely due to shutting down
|
||||
reportError( e ); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,137 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.tcp; |
||||
|
||||
import java.io.*; |
||||
import java.net.*; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
|
||||
/** |
||||
* A straight forward socket-based connector implementation that |
||||
* does not use any separate threading. It relies completely on |
||||
* the buffering in the OS network layer. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class SocketConnector implements Connector |
||||
{ |
||||
private Socket sock; |
||||
private InputStream in; |
||||
private OutputStream out; |
||||
private SocketAddress remoteAddress; |
||||
private byte[] buffer = new byte[65535]; |
||||
|
||||
public SocketConnector( InetAddress address, int port ) throws IOException |
||||
{ |
||||
this.sock = new Socket(address, port); |
||||
remoteAddress = sock.getRemoteSocketAddress(); // for info purposes
|
||||
|
||||
// Disable Nagle's buffering so data goes out when we
|
||||
// put it there.
|
||||
sock.setTcpNoDelay(true); |
||||
|
||||
in = sock.getInputStream(); |
||||
out = sock.getOutputStream(); |
||||
} |
||||
|
||||
protected void checkClosed() |
||||
{ |
||||
if( sock == null ) |
||||
throw new ConnectorException( "Connection is closed:" + remoteAddress ); |
||||
} |
||||
|
||||
public boolean isConnected() |
||||
{ |
||||
if( sock == null ) |
||||
return false; |
||||
return sock.isConnected(); |
||||
} |
||||
|
||||
public void close() |
||||
{ |
||||
checkClosed(); |
||||
try { |
||||
Socket temp = sock; |
||||
sock = null; |
||||
temp.close(); |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error closing socket for:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
|
||||
public boolean available() |
||||
{ |
||||
checkClosed(); |
||||
try { |
||||
return in.available() > 0; |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error retrieving data availability for:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
|
||||
public ByteBuffer read() |
||||
{ |
||||
checkClosed(); |
||||
|
||||
try { |
||||
// Read what we can
|
||||
int count = in.read(buffer); |
||||
if( count < 0 ) { |
||||
// Socket it closed
|
||||
close(); |
||||
return null; |
||||
} |
||||
|
||||
// Wrap it in a ByteBuffer for the caller
|
||||
return ByteBuffer.wrap( buffer, 0, count ); |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
|
||||
public void write( ByteBuffer data ) |
||||
{ |
||||
checkClosed(); |
||||
|
||||
try { |
||||
out.write(data.array(), data.position(), data.remaining()); |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error writing to connection:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,137 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.udp; |
||||
|
||||
import java.io.*; |
||||
import java.net.*; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
|
||||
/** |
||||
* A straight forward datagram socket-based UDP connector |
||||
* implementation. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class UdpConnector implements Connector |
||||
{ |
||||
private DatagramSocket sock = new DatagramSocket(); |
||||
private SocketAddress remoteAddress; |
||||
private byte[] buffer = new byte[65535]; |
||||
|
||||
/** |
||||
* In order to provide proper available() checking, we |
||||
* potentially queue one datagram. |
||||
*/ |
||||
private DatagramPacket pending; |
||||
|
||||
/** |
||||
* Creates a new UDP connection that send datagrams to the |
||||
* specified address and port. |
||||
*/ |
||||
public UdpConnector( InetAddress local, int localPort, |
||||
InetAddress remote, int remotePort ) throws IOException |
||||
{ |
||||
this.sock = new DatagramSocket( localPort, local ); |
||||
remoteAddress = new InetSocketAddress( remote, remotePort ); |
||||
|
||||
// Setup to receive only from the remote address
|
||||
sock.connect( remoteAddress ); |
||||
} |
||||
|
||||
protected void checkClosed() |
||||
{ |
||||
if( sock == null ) |
||||
throw new ConnectorException( "Connection is closed:" + remoteAddress ); |
||||
} |
||||
|
||||
public boolean isConnected() |
||||
{ |
||||
if( sock == null ) |
||||
return false; |
||||
return sock.isConnected(); |
||||
} |
||||
|
||||
public void close() |
||||
{ |
||||
checkClosed(); |
||||
DatagramSocket temp = sock; |
||||
sock = null; |
||||
temp.close(); |
||||
} |
||||
|
||||
/** |
||||
* This always returns false since the simple DatagramSocket usage |
||||
* cannot be run in a non-blocking way. |
||||
*/ |
||||
public boolean available() |
||||
{ |
||||
// It would take a separate thread or an NIO Selector based implementation to get this
|
||||
// to work. If a polling strategy is never employed by callers then it doesn't
|
||||
// seem worth it to implement all of that just for this method.
|
||||
checkClosed(); |
||||
return false; |
||||
} |
||||
|
||||
public ByteBuffer read() |
||||
{ |
||||
checkClosed(); |
||||
|
||||
try { |
||||
DatagramPacket packet = new DatagramPacket( buffer, buffer.length ); |
||||
sock.receive(packet); |
||||
|
||||
// Wrap it in a ByteBuffer for the caller
|
||||
return ByteBuffer.wrap( buffer, 0, packet.getLength() ); |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
|
||||
public void write( ByteBuffer data ) |
||||
{ |
||||
checkClosed(); |
||||
|
||||
try { |
||||
DatagramPacket p = new DatagramPacket( data.array(), data.position(), data.remaining(), |
||||
remoteAddress ); |
||||
sock.send(p); |
||||
} catch( IOException e ) { |
||||
throw new ConnectorException( "Error writing to connection:" + remoteAddress, e ); |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,109 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.udp; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.*; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
|
||||
/** |
||||
* Endpoint implementation that encapsulates the |
||||
* UDP connection information for return messaging, |
||||
* identification of envelope sources, etc. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class UdpEndpoint implements Endpoint |
||||
{ |
||||
private long id; |
||||
private SocketAddress address; |
||||
private DatagramSocket socket; |
||||
private UdpKernel kernel; |
||||
|
||||
public UdpEndpoint( UdpKernel kernel, long id, SocketAddress address, DatagramSocket socket ) |
||||
{ |
||||
this.id = id; |
||||
this.address = address; |
||||
this.socket = socket; |
||||
this.kernel = kernel; |
||||
} |
||||
|
||||
public Kernel getKernel() |
||||
{ |
||||
return kernel; |
||||
} |
||||
|
||||
protected SocketAddress getRemoteAddress() |
||||
{ |
||||
return address; |
||||
} |
||||
|
||||
public void close() |
||||
{ |
||||
try { |
||||
kernel.closeEndpoint(this); |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error closing endpoint for socket:" + socket, e ); |
||||
} |
||||
} |
||||
|
||||
public long getId() |
||||
{ |
||||
return id; |
||||
} |
||||
|
||||
public boolean isConnected() |
||||
{ |
||||
return socket.isConnected(); |
||||
} |
||||
|
||||
public void send( ByteBuffer data ) |
||||
{ |
||||
try { |
||||
DatagramPacket p = new DatagramPacket( data.array(), data.position(), |
||||
data.remaining(), address ); |
||||
socket.send(p); |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error sending datagram to:" + address, e ); |
||||
} |
||||
} |
||||
|
||||
public String toString() |
||||
{ |
||||
return "UdpEndpoint[" + id + ", " + socket + "]"; |
||||
} |
||||
} |
@ -0,0 +1,219 @@ |
||||
/* |
||||
* Copyright (c) 2011 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.kernel.udp; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.*; |
||||
import java.nio.ByteBuffer; |
||||
import java.util.*; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import com.jme3.network.kernel.*; |
||||
|
||||
/** |
||||
* A Kernel implementation using UDP packets. |
||||
* |
||||
* @version $Revision$ |
||||
* @author Paul Speed |
||||
*/ |
||||
public class UdpKernel extends AbstractKernel |
||||
{ |
||||
private InetAddress host; |
||||
private int port; |
||||
private HostThread thread; |
||||
|
||||
// The nature of UDP means that even through a firewall,
|
||||
// a user would have to have a unique address+port since UDP
|
||||
// can't really be NAT'ed.
|
||||
private Map<SocketAddress,UdpEndpoint> socketEndpoints = new ConcurrentHashMap<SocketAddress,UdpEndpoint>(); |
||||
|
||||
public UdpKernel( InetAddress host, int port ) |
||||
{ |
||||
this.host = host; |
||||
this.port = port; |
||||
} |
||||
|
||||
public UdpKernel( int port ) throws IOException |
||||
{ |
||||
this( InetAddress.getLocalHost(), port ); |
||||
} |
||||
|
||||
protected HostThread createHostThread() |
||||
{ |
||||
return new HostThread(); |
||||
} |
||||
|
||||
public void initialize() |
||||
{ |
||||
if( thread != null ) |
||||
throw new IllegalStateException( "Kernel already initialized." ); |
||||
|
||||
thread = createHostThread(); |
||||
|
||||
try { |
||||
thread.connect(); |
||||
thread.start(); |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error hosting:" + host + " port:" + port, e ); |
||||
} |
||||
} |
||||
|
||||
public void terminate() throws InterruptedException |
||||
{ |
||||
if( thread == null ) |
||||
throw new IllegalStateException( "Kernel not initialized." ); |
||||
|
||||
try { |
||||
thread.close(); |
||||
thread = null; |
||||
} catch( IOException e ) { |
||||
throw new KernelException( "Error closing host connection:" + host + " port:" + port, e ); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Dispatches the data to all endpoints managed by the |
||||
* kernel. 'routing' is currently ignored. |
||||
*/ |
||||
public void broadcast( Object routing, ByteBuffer data, boolean reliable ) |
||||
{ |
||||
if( reliable ) |
||||
throw new UnsupportedOperationException( "Reliable send not supported by this kernel." ); |
||||
|
||||
// Hand it to all of the endpoints that match our routing
|
||||
for( UdpEndpoint p : socketEndpoints.values() ) { |
||||
// Does it match the routing information? FIXME
|
||||
|
||||
// Send the data
|
||||
p.send( data ); |
||||
} |
||||
} |
||||
|
||||
protected Endpoint getEndpoint( SocketAddress address, boolean create ) |
||||
{ |
||||
UdpEndpoint p = socketEndpoints.get(address); |
||||
if( p == null && create ) { |
||||
p = new UdpEndpoint( this, nextEndpointId(), address, thread.getSocket() ); |
||||
socketEndpoints.put( address, p ); |
||||
|
||||
// Add an event for it.
|
||||
addEvent( EndpointEvent.createAdd( this, p ) ); |
||||
} |
||||
return p; |
||||
} |
||||
|
||||
/** |
||||
* Called by the endpoints when they need to be closed. |
||||
*/ |
||||
protected void closeEndpoint( UdpEndpoint p ) throws IOException |
||||
{ |
||||
// Just book-keeping to do here.
|
||||
socketEndpoints.remove( p.getRemoteAddress() ); |
||||
|
||||
addEvent( EndpointEvent.createRemove( this, p ) ); |
||||
} |
||||
|
||||
protected void newData( DatagramPacket packet ) |
||||
{ |
||||
// So the tricky part here is figuring out the endpoint and
|
||||
// whether it's new or not. In these UDP schemes, firewalls have
|
||||
// to be ported back to a specific machine so we will consider
|
||||
// the address + port (ie: SocketAddress) the defacto unique
|
||||
// ID.
|
||||
Endpoint p = getEndpoint( packet.getSocketAddress(), true ); |
||||
|
||||
// We'll copy the data to trim it.
|
||||
byte[] data = new byte[packet.getLength()]; |
||||
System.arraycopy(packet.getData(), 0, data, 0, data.length); |
||||
|
||||
Envelope env = new Envelope( p, data, false ); |
||||
addEnvelope( env ); |
||||
} |
||||
|
||||
protected class HostThread extends Thread |
||||
{ |
||||
private DatagramSocket socket; |
||||
private AtomicBoolean go = new AtomicBoolean(true); |
||||
|
||||
private byte[] buffer = new byte[65535]; // slightly bigger than needed.
|
||||
|
||||
public HostThread() |
||||
{ |
||||
setName( "UDP Host@" + host + ":" + port ); |
||||
setDaemon(true); |
||||
} |
||||
|
||||
protected DatagramSocket getSocket() |
||||
{ |
||||
return socket; |
||||
} |
||||
|
||||
public void connect() throws IOException |
||||
{ |
||||
socket = new DatagramSocket( port, host ); |
||||
} |
||||
|
||||
public void close() throws IOException, InterruptedException |
||||
{ |
||||
// Set the thread to stop
|
||||
go.set(false); |
||||
|
||||
// Make sure the channel is closed
|
||||
socket.close(); |
||||
|
||||
// And wait for it
|
||||
join(); |
||||
} |
||||
|
||||
public void run() |
||||
{ |
||||
// An atomic is safest and costs almost nothing
|
||||
while( go.get() ) { |
||||
try { |
||||
// Could reuse the packet but I don't see the
|
||||
// point and it may lead to subtle bugs if not properly
|
||||
// reset.
|
||||
DatagramPacket packet = new DatagramPacket( buffer, buffer.length ); |
||||
socket.receive(packet); |
||||
|
||||
newData( packet ); |
||||
} catch( IOException e ) { |
||||
if( !go.get() ) |
||||
return; |
||||
reportError( e ); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue