/** * 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; }
protected HostThread createHostThread() { return new HostThread(); }
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; }
public void initialize() { if( thread != null ) throw new IllegalStateException( "Kernel already initialized." ); writer = Executors.newFixedThreadPool(2, new NamedThreadFactory(toString() + "-writer")); thread = createHostThread(); try { thread.connect(); thread.start(); } catch( IOException e ) { throw new KernelException( "Error hosting:" + address, e ); } }
/** * Called by the endpoints when they need to be closed. */ protected void closeEndpoint( UdpEndpoint p ) throws IOException { // Just book-keeping to do here. if( socketEndpoints.remove( p.getRemoteAddress() ) == null ) return; log.log( Level.FINE, "Closing endpoint:{0}.", p ); log.log( Level.FINE, "Socket endpoints size:{0}", socketEndpoints.size() ); addEvent( EndpointEvent.createRemove( this, p ) ); wakeupReader(); }
public void close( boolean flush ) { // No real reason to flush UDP traffic yet... especially // when considering that the outbound UDP isn't even // queued. try { kernel.closeEndpoint(this); connected = false; } catch( IOException e ) { throw new KernelException( "Error closing endpoint for socket:" + socket, e ); } }
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 ); }
public void send( ByteBuffer data ) { if( !isConnected() ) { throw new KernelException( "Endpoint is not connected:" + this ); } try { DatagramPacket p = new DatagramPacket( data.array(), data.position(), data.remaining(), address ); // Just queue it up for the kernel threads to write // out kernel.enqueueWrite( this, p ); //socket.send(p); } catch (Exception e) { if (e instanceof SocketException) { throw new KernelException("Error sending datagram to:" + address, e); } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } }
public void terminate() throws InterruptedException { if( thread == null ) throw new IllegalStateException( "Kernel not initialized." ); try { thread.close(); writer.shutdown(); thread = null; // Need to let any caller waiting for a read() wakeup wakeupReader(); } catch( IOException e ) { throw new KernelException( "Error closing host connection:" + address, e ); } }
public void connectToServer( InetAddress address, int port, int remoteUdpPort ) throws IOException { UdpConnector fast = new UdpConnector( address, remoteUdpPort ); SocketConnector reliable = new SocketConnector( address, port ); setPrimaryConnectors( reliable, fast, new TcpConnectorFactory(address) ); } }
protected void enqueueWrite( Endpoint endpoint, DatagramPacket packet ) { writer.execute( new MessageWriter(endpoint, packet) ); }
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() { log.log( Level.FINE, "Kernel started for connection:{0}.", address ); // 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 ); } } } }
/** * Creates a named and versioned Server that will utilize both reliable and fast * transports to communicate with clients. The specified port * will be used for both TCP and UDP communication. * * @param gameName This is the name that identifies the game. Connecting clients * must use this name or be turned away. * @param version This is a game-specific verison that helps detect when out-of-date * clients have connected to an incompatible server. * @param tcpPort The port upon which the TCP hosting will listen for new connections. * @param udpPort The port upon which the UDP hosting will listen for new 'fast' UDP * messages. Set to -1 if 'fast' traffic should go over TCP. This will * completely disable UDP traffic for this server. */ public static Server createServer( String gameName, int version, int tcpPort, int udpPort ) throws IOException { UdpKernel fast = udpPort == -1 ? null : new UdpKernel(udpPort); SelectorKernel reliable = new SelectorKernel(tcpPort); return new DefaultServer( gameName, version, reliable, fast ); }
public void run() { // Not guaranteed to always work but an extra datagram // to a dead connection isn't so big of a deal. if( !endpoint.isConnected() ) { return; } try { thread.getSocket().send(packet); } catch( Exception e ) { KernelException exc = new KernelException( "Error sending datagram to:" + address, e ); exc.fillInStackTrace(); reportError(exc); } } }
public void close() { checkClosed(); DatagramSocket temp = sock; sock = null; connected.set(false); temp.close(); }
/** * Creates a Client that communicates with the specified host and and separate TCP and UDP ports * using both reliable and fast transports. * * @param gameName This is the name that identifies the game. This must match * the target server's name or this client will be turned away. * @param version This is a game-specific verison that helps detect when out-of-date * clients have connected to an incompatible server. This must match * the server's version of this client will be turned away. * @param hostPort The remote TCP port on the server to which this client should * send reliable messages. * @param remoteUdpPort The remote UDP port on the server to which this client should * send 'fast'/unreliable messages. Set to -1 if 'fast' traffic should * go over TCP. This will completely disable UDP traffic for this * client. */ public static Client connectToServer( String gameName, int version, String host, int hostPort, int remoteUdpPort ) throws IOException { InetAddress remoteAddress = InetAddress.getByName(host); UdpConnector fast = remoteUdpPort == -1 ? null : new UdpConnector( remoteAddress, remoteUdpPort ); SocketConnector reliable = new SocketConnector( remoteAddress, hostPort ); return new DefaultClient( gameName, version, reliable, fast, new TcpConnectorFactory(remoteAddress) ); }
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 ); } } }
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 ) { if( !connected.get() ) { // Nothing to see here... just move along return null; } throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e ); } }