ConnectionManager allowed a server to listen on multiple tcp and datagram ports, but it only closed

the last opened.  Close all opened listening sockets.

Also, the last opened datagram socket would be used to send all datagram messages instead of the one
datagrams were received on.  It now goes through the same channel where datagrams were last
received.



git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6156 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Charlie Groves
2010-09-14 22:47:45 +00:00
parent c2ebf2884b
commit 0d4837e71d
2 changed files with 50 additions and 41 deletions
@@ -21,17 +21,16 @@
package com.threerings.presents.server.net;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import com.threerings.io.FramedInputStream;
import com.threerings.io.FramingOutputStream;
@@ -146,6 +145,15 @@ public class Connection implements NetEventHandler
return _datagramAddress;
}
/**
* Returns the channel through which datagrams should be sent or null if no datagram channel
* has been established.
*/
public DatagramChannel getDatagramChannel ()
{
return _datagramChannel;
}
/**
* Sets the secret string used to authenticate datagrams from the client.
*/
@@ -236,7 +244,8 @@ public class Connection implements NetEventHandler
/**
* Processes a datagram sent to this connection.
*/
public void handleDatagram (InetSocketAddress source, ByteBuffer buf, long when)
public void handleDatagram (InetSocketAddress source, DatagramChannel channel,
ByteBuffer buf, long when)
{
// lazily create our various bits and bobs
if (_digest == null) {
@@ -263,6 +272,7 @@ public class Connection implements NetEventHandler
// update our target address
_datagramAddress = source;
_datagramChannel = channel;
// read the contents through the sequencer
try {
@@ -450,6 +460,7 @@ public class Connection implements NetEventHandler
protected ObjectOutputStream _oout;
protected InetSocketAddress _datagramAddress;
protected DatagramChannel _datagramChannel;
protected byte[] _datagramSecret;
protected boolean _transmitDatagrams;
@@ -34,7 +34,6 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@@ -116,20 +115,15 @@ public class ConnectionManager extends LoopingThread
String bindHostname, String datagramHostname, int[] ports, int[] datagramPorts)
throws IOException
{
Preconditions.checkArgument(ports != null, "Ports must be non-null.");
Preconditions.checkArgument(datagramPorts != null, "Datagram ports must be non-null. " +
Preconditions.checkNotNull(ports, "Ports must be non-null.");
Preconditions.checkNotNull(datagramPorts, "Datagram ports must be non-null. " +
"Pass a zero-length array to bind no datagram ports.");
Preconditions.checkState(!super.isRunning(), "Must initialize before starting.");
_bindHostname = bindHostname;
_datagramHostname = datagramHostname;
_ports = ports;
_datagramPorts = datagramPorts;
_selector = SelectorProvider.provider().openSelector();
// create our stats record
_stats = new ConMgrStats();
_lastStats = new ConMgrStats();
}
/**
@@ -360,18 +354,20 @@ public class ConnectionManager extends LoopingThread
@Override
protected void willStart ()
{
Preconditions.checkNotNull(_ports, "Must call init before starting.");
int successes = 0;
for (int port : _ports) {
try {
// create a listening socket and add it to the select set
_ssocket = ServerSocketChannel.open();
_ssocket.configureBlocking(false);
ServerSocketChannel ssocket = ServerSocketChannel.open();
ssocket.configureBlocking(false);
InetSocketAddress isa = getAddress(_bindHostname, port);
_ssocket.socket().bind(isa);
registerChannel(_ssocket);
ssocket.socket().bind(isa);
registerChannel(ssocket);
successes++;
log.info("Server listening on " + isa + ".");
_ssockets.add(ssocket);
} catch (IOException ioe) {
log.warning("Failure listening to socket", "hostname", _bindHostname,
@@ -416,12 +412,13 @@ public class ConnectionManager extends LoopingThread
for (int port : _datagramPorts) {
try {
// create a channel and add it to the select set
_datagramChannel = DatagramChannel.open();
_datagramChannel.socket().setTrafficClass(0x10); // IPTOS_LOWDELAY
_datagramChannel.configureBlocking(false);
DatagramChannel channel = DatagramChannel.open();
channel.socket().setTrafficClass(0x10); // IPTOS_LOWDELAY
channel.configureBlocking(false);
InetSocketAddress isa = getAddress(_datagramHostname, port);
_datagramChannel.socket().bind(isa);
registerChannel(_datagramChannel);
channel.socket().bind(isa);
registerChannel(channel);
_datagramChannels.add(channel);
log.info("Server accepting datagrams on " + isa + ".");
} catch (IOException ioe) {
@@ -815,7 +812,7 @@ public class ConnectionManager extends LoopingThread
_databuf.clear();
_databuf.put(data).flip();
try {
return _datagramChannel.send(_databuf, target) > 0;
return conn.getDatagramChannel().send(_databuf, target) > 0;
} catch (IOException ioe) {
log.warning("Failed to send datagram.", ioe);
return false;
@@ -849,10 +846,9 @@ public class ConnectionManager extends LoopingThread
// create a new authing connection object to manage the authentication of this client
// connection and register it with our selection set
SelectableChannel selchan = channel;
selchan.configureBlocking(false);
channel.configureBlocking(false);
AuthingConnection aconn = new AuthingConnection();
aconn.selkey = selchan.register(_selector, SelectionKey.OP_READ);
aconn.selkey = channel.register(_selector, SelectionKey.OP_READ);
aconn.init(this, channel, System.currentTimeMillis());
_handlers.put(aconn.selkey, aconn);
synchronized (this) {
@@ -909,7 +905,7 @@ public class ConnectionManager extends LoopingThread
int connectionId = _databuf.getInt();
Connection conn = _connections.get(connectionId);
if (conn != null) {
conn.handleDatagram(source, _databuf, when);
conn.handleDatagram(source, listener, _databuf, when);
} else {
log.debug("Received datagram for unknown connection", "id", connectionId,
"source", source);
@@ -1074,16 +1070,18 @@ public class ConnectionManager extends LoopingThread
// unbind our listening socket
// Note: because we wait for the object manager to exit before we do, we will still be
// accepting connections as long as there are events pending.
// TODO: consider shutting down the listen socker earlier, like in the shutdown method
try {
_ssocket.socket().close();
} catch (IOException ioe) {
log.warning("Failed to close listening socket.", ioe);
// TODO: consider closing the listen sockets earlier, like in the shutdown method
for (ServerSocketChannel ssocket : _ssockets) {
try {
ssocket.socket().close();
} catch (IOException ioe) {
log.warning("Failed to close listening socket: " + ssocket, ioe);
}
}
// and the datagram socket, if any
if (_datagramChannel != null) {
_datagramChannel.socket().close();
// and the datagram sockets, if any
for (DatagramChannel datagramChannel : _datagramChannels) {
datagramChannel.socket().close();
}
// report if there's anything left on the outgoing message queue
@@ -1224,8 +1222,8 @@ public class ConnectionManager extends LoopingThread
protected int[] _ports, _datagramPorts;
protected String _bindHostname, _datagramHostname;
protected Selector _selector;
protected ServerSocketChannel _ssocket;
protected DatagramChannel _datagramChannel;
protected List<ServerSocketChannel> _ssockets = Lists.newArrayList();
protected List<DatagramChannel> _datagramChannels = Lists.newArrayList();
/** Counts consecutive runtime errors in select(). */
protected int _runtimeExceptionCount;
@@ -1251,10 +1249,10 @@ public class ConnectionManager extends LoopingThread
protected Map<Connection, OverflowQueue> _oflowqs = Maps.newHashMap();
/** Our current runtime stats. */
protected ConMgrStats _stats;
protected ConMgrStats _stats = new ConMgrStats();
/** A snapshot of our runtime stats as of our last report. */
protected ConMgrStats _lastStats;
protected ConMgrStats _lastStats = new ConMgrStats();
/** Used to periodically report connection manager activity when in debug mode. */
protected long _lastDebugStamp;