/** * Registers a new channel with this Selector. If we've already stored over the limit of channels, * this will store the channel in a temporary list to be cancelled on the next selection. */ @Override protected synchronized SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) { int connID; if (!(ch instanceof UDPSocketChannel)) throw new IllegalSelectorException(); UDPSocketChannel channel = (UDPSocketChannel) ch; UDPSocketChannel[] copy = new UDPSocketChannel[_channels.length]; for (int i = 0; i < _channels.length; i++) copy[i] = _channels[i]; for (int i = 1; i <= copy.length; i++) { connID = (_lastConnectionID + i) % 256; // We don't assign zero. if (connID == 0) continue; // If the slot is open, take it. if (copy[connID] == null) { _lastConnectionID = connID; copy[connID] = channel; channel.getProcessor().setConnectionId((byte) connID); _channels = copy; return new UDPSelectionKey(this, att, ch, ops); } } // We don't have enough space for this connection. Add it to a temporary // list of bad connections which will be removed during selectNow. LOG.warn("Attempting to add over connection limit"); channelsToRemove.add(ch); return new UDPSelectionKey(this, att, ch, ops); }
/** Polls through all available channels and returns those that are ready. */ @Override public int selectNow() throws IOException { UDPSocketChannel[] array = _channels; UDPSocketChannel[] removed = null; selectedKeys.clear(); for (int i = 0; i < array.length; i++) { UDPSocketChannel channel = array[i]; if (channel == null) continue; UDPSelectionKey key = (UDPSelectionKey) channel.keyFor(this); if (key != null) { if (key.isValid() && channel.isOpen()) { int currentOps = channel.getProcessor().readyOps(); int readyOps = currentOps & key.interestOps(); if (readyOps != 0) { key.setReadyOps(readyOps); selectedKeys.add(key); } } else { if (removed == null) removed = new UDPSocketChannel[array.length]; removed[i] = channel; } } } // Go through the removed list & remove them from _connections. // _connections may have changed (since we didn't lock while polling), // so we need to check and ensure the given UDPConnectionProcessor // is the same. synchronized (this) { if (removed != null) { UDPSocketChannel[] copy = new UDPSocketChannel[_channels.length]; for (int i = 0; i < _channels.length; i++) { if (_channels[i] == removed[i]) copy[i] = null; else copy[i] = _channels[i]; } _channels = copy; } if (!channelsToRemove.isEmpty()) { for (SelectableChannel next : channelsToRemove) { UDPSelectionKey key = (UDPSelectionKey) next.keyFor(this); key.cancel(); key.setReadyOps(0); selectedKeys.add(key); } channelsToRemove.clear(); } } return selectedKeys.size(); }
/** Determines if we're connected to the given host. */ public boolean isConnectedTo(InetAddress host) { UDPSocketChannel[] array = _channels; if (_lastConnectionID == 0) return false; for (int i = 0; i < array.length; i++) { UDPSocketChannel channel = array[i]; if (channel != null && host.equals(channel.getRemoteSocketAddress().getAddress())) { return true; } } return false; }
/** * Route a message to the {@link UDPConnectionProcessor} identified via the message's connection * ID. Notifies the provided listener (if any) if the channel is ready to produce events. */ public void routeMessage(RUDPMessage msg, InetSocketAddress addr) { UDPSocketChannel[] array = _channels; int connID = msg.getConnectionID() & 0xff; UDPSocketChannel channel = null; // If connID equals 0 and SynMessage then associate with a connection // that appears to want it (connecting and with knowledge of it). if (connID == 0 && msg instanceof SynMessage) { LOG.debugf("route sym: {0}", msg); for (int i = 1; i < array.length; i++) { channel = array[i]; if (channel == null) continue; LOG.debugf("non-empty index: {0}, addr: {1}", i, channel.getRemoteSocketAddress()); if (channel.isConnectionPending() && channel.isForMe(addr, (SynMessage) msg)) { LOG.debugf( "found index: {0}, sender id: {1}", i, ((SynMessage) msg).getSenderConnectionID()); channel.getProcessor().handleMessage(msg); break; } } // Note: eventually these messages should find a match // so it is safe to throw away premature ones } else if (array[connID] != null) { // If valid connID then send on to connection channel = array[connID]; if (msg instanceof SynMessage) { LOG.debugf("already assigned syn: {0}", msg); } if (channel.getRemoteSocketAddress().equals(addr)) channel.getProcessor().handleMessage(msg); } else { LOG.debugf("message for non-existing connection: {0}", msg); } if (channel != null && channel.getProcessor().readyOps() != 0) context.getTransportListener().eventPending(); }