--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.message;
+
+/**
+ * Message used to authenticate a login with the SocialServer.
+ *
+ * @author eddie
+ */
+public class MessageLogin extends MessageAbstract {
+
+ private static final String _type = "Login";
+
+ public final String _uName;
+
+ public final String _uPass;
+
+ public long _userID = 0;
+
+ public boolean _loggedIn = false;
+
+ public static String getType() {
+ return _type;
+ }
+
+ @Override
+ public String getMessageType() {
+ return _type;
+ }
+
+ public MessageLogin() {
+ this._uName = null;
+ this._uPass = null;
+ }
+
+ public MessageLogin (String uName, String uPass) {
+ this._uName = uName;
+ this._uPass = uPass;
+ }
+
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.message;
+
+/**
+ * Message used to notify a member's state to other members.
+ *
+ * @author eddie
+ */
+public class MessageMemberState extends MessageAbstract {
+
+ private static final String _type = "MemberState";
+
+ public long _userID;
+
+ public boolean _onlineStatus;
+
+ public static String getType() {
+ return _type;
+ }
+
+ @Override
+ public String getMessageType() {
+ return _type;
+ }
+
+ public MessageMemberState() {
+ this._userID = 0;
+ this._onlineStatus = false;
+ }
+
+ public MessageMemberState(long userID, boolean state) {
+ this._userID = userID;
+ this._onlineStatus = state;
+ }
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.message;
+
+import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkStream;
+/**
+ * Pass a NetworkStream reference in a message.
+ *
+ * @author eddie
+ */
+public class MessageNetworkStream extends MessageAbstract {
+ public static final String _type = "NetworkStream";
+
+ public final NetworkStream _stream;
+
+ public static String getType() {
+ return _type;
+ }
+
+ @Override
+ public String getMessageType() {
+ return _type;
+ }
+
+ public MessageNetworkStream(NetworkStream stream) {
+ super();
+ _stream = stream;
+ }
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+import java.util.EventObject;
+
+/**
+ *
+ * @author eddie
+ */
+public class ConnectionEstablishedEvent extends EventObject {
+
+ private final NetworkStream _stream;
+
+ public ConnectionEstablishedEvent(Object source, NetworkStream stream) {
+ super(source);
+ _stream = stream;
+ }
+
+ public NetworkStream getStream() {
+ return _stream;
+ }
+
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+/**
+ *
+ * @author eddie
+ */
+public interface ConnectionEstablishedEventListener {
+ public void connectionEstablished(ConnectionEstablishedEvent event);
+}
* Filled in by the message originator with the title of the destination service
*/
String _serviceTarget;
+
+ /**
+ * Optional user key of a NetworkStream to enable TCP to send userID on connection
+ */
+ long _key;
Class<?> _class;
MessageAbstract _message;
* contain native types that are serializable
* @throws IllegalArgumentException
*/
- NetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
+ public NetworkMessage(String intent, String target, MessageAbstract message) throws IllegalArgumentException {
_serializeLength = -1;
if ( !(intent != null && intent.length() > 0) )
throw(new IllegalArgumentException("intent cannot be null or empty"));
return _serializeLength;
}
+ /**
+ * Set the key of the TCP stream in the messsage.
+ *
+ * @param key
+ */
+ public void setKey(long key) {
+ _key = key;
+ }
+
+ /**
+ * Get the key set by the ServerTCP.
+ *
+ * @return the key of the stream associated
+ */
+ public long getKey() {
+ return _key;
+ }
+
/**
* Create a message for passing over the network.
*
* @see NetworkMessage
* @see NetworkMessageEvent
* @see NetworkMessageEventListener
+ * @see NetworkMessageEventListenerManager
* @author TJ <hacker@iam.tj>
*/
public interface NetworkMessageEventGenerator {
*
* @param listener
*/
- public void removeNetworkMessageEventListener(NetworkMessageEventListener listener);
+ public void removeNetworkMessageEventListener(NetworkMessageEventListener listener);
+
+ /**
+ * Sends message to all registered listeners.
+ *
+ * @param message
+ */
+ public void fireNetworkMessageEvent(NetworkMessage message);
}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+import java.util.ArrayList;
+
+/**
+ *
+ * @author eddie
+ */
+public class NetworkMessageEventListenerManager implements NetworkMessageEventGenerator {
+ /**
+ * Wrapper allowing filtering of NetworkMessageEvents based on message intent.
+ */
+ public class NetworkMessageEventListenerWithIntent {
+ String _intent;
+ NetworkMessageEventListener _listener;
+
+ public NetworkMessageEventListenerWithIntent(NetworkMessageEventListener listener, String intent) {
+ _intent = intent;
+ _listener = listener;
+ }
+ }
+ protected ArrayList<NetworkMessageEventListenerWithIntent> _NetworkMessageEventListeners;
+
+ /**
+ * Construct an empty list of Event Listeners.
+ */
+ NetworkMessageEventListenerManager() {
+ _NetworkMessageEventListeners = new ArrayList<>();
+ }
+
+ /**
+ * Add a NetworkMessageEvent listener.
+ *
+ * Listens to all intents.
+ *
+ * @param listener
+ */
+ @Override
+ public synchronized void addNetworkMessageEventListener(NetworkMessageEventListener listener) {
+ _NetworkMessageEventListeners.add(new NetworkMessageEventListenerWithIntent(listener, null));
+ }
+
+ /**
+ * Add a filtered NetworkMessageEvent listener.
+ *
+ * Filters on the intent of the NetworkMessage.
+ *
+ * @param listener
+ * @param intent null to listen to all intents, otherwise the intent to listen for
+ */
+ @Override
+ public synchronized void addNetworkMessageEventListener(NetworkMessageEventListener listener, String intent) {
+ _NetworkMessageEventListeners.add(new NetworkMessageEventListenerWithIntent(listener, intent));
+ }
+
+ /**
+ * Remove a NetworkMessageEvent listener.
+ *
+ * @param listener
+ */
+ @Override
+ public synchronized void removeNetworkMessageEventListener(NetworkMessageEventListener listener) {
+ for (NetworkMessageEventListenerWithIntent intentListener : _NetworkMessageEventListeners)
+ if (intentListener._listener == listener)
+ _NetworkMessageEventListeners.remove(intentListener);
+ }
+
+ /**
+ * Send a NetworkMessageEvent to all listeners.
+ *
+ * Only sends the message to listeners registered for the same intent, or for all messages.
+ *
+ * @param message the NetworkMessage to send
+ */
+ @Override
+ public synchronized void fireNetworkMessageEvent(NetworkMessage message) {
+ NetworkMessageEvent event = new NetworkMessageEvent(this, message);
+ for (NetworkMessageEventListenerWithIntent intentListener : _NetworkMessageEventListeners) {
+ if (intentListener._intent.equals(message._intent) || intentListener._intent == null)
+ intentListener._listener.NetworkMessageReceived(event);
+ }
+ }
+}
*
* @author TJ <hacker@iam.tj>
*/
-public abstract class NetworkServerAbstract extends SwingWorker<Integer, NetworkMessage> implements NetworkMessageEventGenerator {
+public abstract class NetworkServerAbstract extends SwingWorker<Integer, NetworkMessage> {
/**
* Single Logger for the class used by all object instances.
WSYD_SocketAddress _socketAddress;
protected ServiceAddressMap _serviceToHostMap;
- /**
- * Thread safe First In, First Out Queue of NetworkMessage objects waiting to be sent.
- *
- * Allows the Owner Thread to submit new messages for sending that the Worker Thread
- * can safely access.
- */
- protected ConcurrentLinkedQueue<NetworkMessage> _sendMessageQueue = new ConcurrentLinkedQueue<>();
-
- /**
- * Wrapper for filtering NetworkMessageEvents based on the message intent
- */
- public class NetworkMessageEventListenerWithIntent {
- String _intent;
- NetworkMessageEventListener _listener;
-
- public NetworkMessageEventListenerWithIntent(NetworkMessageEventListener listener, String intent) {
- _intent = intent;
- _listener = listener;
- }
- }
- protected ArrayList<NetworkMessageEventListenerWithIntent> _NetworkMessageEventListeners = new ArrayList<>();
+ private final NetworkMessageEventListenerManager _eventManager;
/**
*
* @param level message importance
this._title = null;
this._socketAddress = null;
this._serviceToHostMap = null;
+ this._eventManager = null;
}
/**
this._title = title;
this._socketAddress = socketAddress;
this._serviceToHostMap = serviceToHostMap;
+ this._eventManager = new NetworkMessageEventListenerManager();
if (LOGGER == null) // do not replace existing logger reference
LOGGER = logger;
}
this._connectionCount++;
// send a queued message
- NetworkMessage temp = this.dequeueMessage();
+ NetworkMessage temp = this.sendMessage();
if (temp != null) {
if (!this.serverSend(temp)) {
logMessages.clear();
return this._connectionCount;
}
-
+ /**
+ * Removes a message from a queue of pending messages.
+ *
+ * This method is called on the Worker Thread by the doInBackground() main loop.
+ *
+ * Sub-classes that have the ability to transmit messages should implement this fully.
+ *
+ * @return a message to be sent
+ */
+ protected abstract NetworkMessage sendMessage();
/**
* Open the socket ready for accepting data or connections.
*
*/
public abstract boolean serverListen();
- /**
- * Removes a message from the queue of pending messages.
- *
- * This method is called on the Worker Thread by the doInBackground() main loop.
- *
- * @return a message to be sent
- */
- protected NetworkMessage dequeueMessage() {
- return this._sendMessageQueue.poll();
- }
-
/* XXX: Methods below here all execute on the GUI Event Dispatch Thread */
+ public NetworkMessageEventListenerManager getEventManager() {
+ return this._eventManager;
+ }
+
/**
* Fetch messages received by the server.
*
* For delivery to event listeners; usually Swing GUI components. This method will run on the
- * Owner Thread so must complete quickly it that is the GUI Event Dispatch Thread.
+ * Owner Thread so must complete quickly as that is the GUI Event Dispatch Thread.
*
* @param list messages received and queued
*/
@Override
protected void process(List<NetworkMessage> list) {
for (NetworkMessage message: list) {
- fireNetworkMessageEvent(message);
+ this._eventManager.fireNetworkMessageEvent(message);
}
}
*/
@Override
protected abstract void done();
-
- /**
- * Adds a message to the queue of pending messages.
- *
- * This method will usually be called from the Owner Thread.
- *
- * @param message to be sent
- * @return true if the message was added to the queue
- * @throws IllegalArgumentException if the target does not exist in the serviceToHost mapping
- */
- public boolean queueMessage(NetworkMessage message) throws IllegalArgumentException {
- boolean result = false;
- if (message != null) {
- // ensure the target is set and is a valid service
- String target = message.getTarget();
- if (target == null)
- throw new IllegalArgumentException("target cannot be null");
- if(!_serviceToHostMap.isServiceValid(target))
- throw new IllegalArgumentException("target service does not exist: " + target);
-
- NetworkMessage temp;
- try { // make a deep clone of the message
- temp = NetworkMessage.clone(message);
- result = this._sendMessageQueue.add(temp);
- } catch (CloneNotSupportedException e) {
- // TODO: queueMessage() log CloneNotSupportedException
- e.printStackTrace();
- }
- }
- return result;
- }
-
- /**
- * Add a NetworkMessageEvent listener.
- *
- * Listens to all intents.
- *
- * @param listener
- */
- @Override
- public synchronized void addNetworkMessageEventListener(NetworkMessageEventListener listener) {
- _NetworkMessageEventListeners.add(new NetworkMessageEventListenerWithIntent(listener, null));
- }
-
- /**
- * Add a filtered NetworkMessageEvent listener.
- *
- * Filters on the intent of the NetworkMessage.
- * @param listener
- * @param intent null to listen to all intents, otherwise the intent to listen for
- */
- @Override
- public synchronized void addNetworkMessageEventListener(NetworkMessageEventListener listener, String intent) {
- _NetworkMessageEventListeners.add(new NetworkMessageEventListenerWithIntent(listener, intent));
- }
-
- /**
- * Remove a NetworkMessageEvent listener.
- *
- * @param listener
- */
- @Override
- public synchronized void removeNetworkMessageEventListener(NetworkMessageEventListener listener) {
- for (NetworkMessageEventListenerWithIntent intentListener : _NetworkMessageEventListeners)
- if (intentListener._listener == listener)
- _NetworkMessageEventListeners.remove(intentListener);
- }
-
- /**
- * Send a NetworkMessageEvent to all listeners.
- *
- * Only sends the message to listeners registered for the same intent, or for all messages.
- *
- * @param message the NetworkMessage to send
- */
- private synchronized void fireNetworkMessageEvent(NetworkMessage message) {
- NetworkMessageEvent event = new NetworkMessageEvent(this, message);
- for (NetworkMessageEventListenerWithIntent intentListener : _NetworkMessageEventListeners) {
- if (intentListener._intent.equals(message._intent) || intentListener._intent == null)
- intentListener._listener.NetworkMessageReceived(event);
- }
- }
}
*/
package uk.ac.ntu.n0521366.wsyd.libs.net;
+import java.io.IOException;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
+import java.net.Socket;
import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import uk.ac.ntu.n0521366.wsyd.libs.message.MessageNetworkStream;
/**
* Dual-use multithreading network TCP server that can be used stand-alone
* @author TJ <hacker@iam.tj>
*/
public class NetworkServerTCP extends NetworkServerAbstract {
+
ServerSocket _serverSocket;
+
+ NetworkStreamManager _streamManager;
+
+ protected ArrayList<ConnectionEstablishedEventListener> _ConnectionEstablishedEventListeners;
+
+ /**
+ * Construct the server with a Logger.
+ *
+ * No socket is opened.
+ *
+ * @param socketAddress The socket to listen on
+ * @param title source identifier for use in log messages and sent NetworkMessage objects
+ * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
+ * @param manager
+ * @param logger An instance of Logger to be used by all objects of this class
+ */
+ public NetworkServerTCP(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap, NetworkStreamManager manager, Logger logger) {
+ super(socketAddress, title, serviceToHostMap, logger);
+ this._streamManager = manager;
+ this._ConnectionEstablishedEventListeners = new ArrayList<>();
+ }
+ /**
+ * Construct the server without a Logger.
+ *
+ * No socket is opened.
+ *
+ * @param socketAddress The socket to listen on
+ * @param title source identifier for use in log messages and sent NetworkMessage objects
+ * @param serviceToHostMap the map object used for host <> InetSocketAddress lookups
+ * @param manager
+ */
+ public NetworkServerTCP(WSYD_SocketAddress socketAddress, String title, ServiceAddressMap serviceToHostMap, NetworkStreamManager manager) {
+ super(socketAddress, title, serviceToHostMap);
+ this._streamManager = manager;
+ }
+
/**
* Open the socket ready for accepting connections.
*
*/
@Override
public void serverOpen() throws SocketException {
+ try {
+ _serverSocket = new ServerSocket(_socketAddress.getPort(), 50, _socketAddress.getAddress());
+ } catch (IOException ex) {
+ throw new SocketException(ex.getMessage());
+ }
+ _serverSocket.setSoTimeout(100);
+ if (_socketAddress.getPort() == Network.PORTS_EPHEMERAL) {
+ // reflect the actual port in use if an ephermal port was requested
+ InetSocketAddress actualSA = (InetSocketAddress)_serverSocket.getLocalSocketAddress();
+ _socketAddress.setAddress(actualSA.getAddress());
+ _socketAddress.setPort(actualSA.getPort());
+ }
+ //log(Level.FINEST, _title, MessageFormat.format("Connection from {0}:{1}", _socketAddress.getAddress().getCanonicalHostName(), Integer.toString(_socketAddress.getPort())));
+ // TODO: Complete this implementation
}
/**
*/
@Override
public void serverClose() throws SocketException {
-
+ if (this._serverSocket != null)
+ try {
+ this._serverSocket.close();
+ } catch (IOException ex) {
+ throw new SocketException(ex.getMessage());
+ }
}
/**
public boolean serverListen() {
boolean result = false;
+ try {
+ //System.err.println("Before");
+ Socket connectionSocket = _serverSocket.accept();
+ NetworkStream newStream = new NetworkStream(connectionSocket, _streamManager);
+ long userID = _streamManager.addStream(0, newStream);
+ // workaround to enable firing a ConnectionEstablishedEvent in process() on the owner thread
+ NetworkMessage temp = new NetworkMessage("triggerConnectionEstablishedEvent", null, new MessageNetworkStream(newStream));
+ publish(temp);
+ log(Level.INFO, _title, MessageFormat.format("Incoming connection from {0}:{1}", connectionSocket.getInetAddress().getCanonicalHostName(), Integer.toString(connectionSocket.getPort())));
+
+ // add or update the last-seen time of the Sender host in the known services map
+ ServiceAddressMap.LastSeenHost host = new ServiceAddressMap.LastSeenHost((InetSocketAddress)connectionSocket.getRemoteSocketAddress());
+ this._serviceToHostMap.put(Long.toString(userID), host);
+ log(Level.INFO, _title, MessageFormat.format("Added \"{0}\" to service map", userID));
+ System.err.println("Added new TCP stream successfully");
+ }
+ catch (SocketTimeoutException e) {
+ //Nothing to be done
+ }
+ catch (IOException e) {
+ // Nothing to be done
+ System.err.println("Incoming connection caused exception...");
+ }
+
return false;
}
+ private void fireConnectionEstablishedEvent(NetworkStream stream) {
+ ConnectionEstablishedEvent event = new ConnectionEstablishedEvent(this, stream);
+ for (ConnectionEstablishedEventListener listener: this._ConnectionEstablishedEventListeners) {
+ listener.connectionEstablished(event);
+ }
+
+ }
+ public synchronized void addConnectionEstablisedEventListener(ConnectionEstablishedEventListener listener) {
+ _ConnectionEstablishedEventListeners.add(listener);
+ }
+
+ public synchronized void removeConnectionEstablishedEventListener(ConnectionEstablishedEventListener listener) {
+ for (ConnectionEstablishedEventListener el : _ConnectionEstablishedEventListeners)
+ if (el == listener) {
+ _ConnectionEstablishedEventListeners.remove(listener);
+ break;
+ }
+ }
+
+
/* XXX: Methods below here all execute on the GUI Event Dispatch Thread */
/**
protected void done() {
}
+
+ @Override
+ protected NetworkMessage sendMessage() {
+ return null;
+ }
+
+ @Override
+ protected void process(List<NetworkMessage> list) {
+ System.err.println("process()");
+ for (NetworkMessage nm: list) {
+ MessageNetworkStream message = (MessageNetworkStream)nm.getMessage();
+ System.err.println("ConnectionEstablished dispatched for " + message._stream._key);
+ fireConnectionEstablishedEvent(message._stream);
+
+ }
+ }
}
import java.net.DatagramPacket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
* Maximum size of UDP packet payload
*/
public static final int UDP_PAYLOAD_SIZE_MAX = 65507;
-
+
+ /**
+ * Thread safe First In, First Out Queue of NetworkMessage objects waiting to be sent.
+ *
+ * Allows the Owner Thread to submit new messages for sending that the Worker Thread
+ * can safely access.
+ */
+ protected ConcurrentLinkedQueue<NetworkMessage> _sendMessageQueue = new ConcurrentLinkedQueue<>();
+
/**
* Construct the server with a Logger.
*
return result;
}
+ /**
+ * Removes a message from the queue of pending messages.
+ *
+ * This method is called on the Worker Thread by the doInBackground() main loop.
+ *
+ * @return a message to be sent
+ */
+ @Override
+ protected NetworkMessage sendMessage() {
+ return this._sendMessageQueue.poll();
+ }
+
+
/* XXX: Methods below here all execute on the GUI Event Dispatch Thread */
/**
protected void done() {
// TODO: done() implement any clean-up after doInBackground() has returned
}
+
+ /**
+ * Adds a message to the queue of pending messages.
+ *
+ * This method will usually be called from the Owner Thread.
+ *
+ * @param message to be sent
+ * @return true if the message was added to the queue
+ * @throws IllegalArgumentException if the target does not exist in the serviceToHost mapping
+ */
+ public boolean queueMessage(NetworkMessage message) throws IllegalArgumentException {
+ boolean result = false;
+ if (message != null) {
+ // ensure the target is set and is a valid service
+ String target = message.getTarget();
+ if (target == null)
+ throw new IllegalArgumentException("target cannot be null");
+ if(!_serviceToHostMap.isServiceValid(target))
+ throw new IllegalArgumentException("target service does not exist: " + target);
+
+ NetworkMessage temp;
+ try { // make a deep clone of the message
+ temp = NetworkMessage.clone(message);
+ result = this._sendMessageQueue.add(temp);
+ } catch (CloneNotSupportedException e) {
+ // TODO: queueMessage() log CloneNotSupportedException
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
}
this._multicastSocket = new MulticastSocket(_socketAddress.getPort());
this._multicastSocket.setTimeToLive(2); // don't traverse more than 2 routers
this._multicastSocket.setSoTimeout(100); // 1/10th second blocking timeout on receive()
- this._multicastSocket.setLoopbackMode(true); // inverted logic; true == disable. Don't want to receive our own sent packets
+ this._multicastSocket.setLoopbackMode(false); // inverted logic; true == disable. Don't want to receive our own sent packets
Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
while (ifs.hasMoreElements()) {
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+/**
+ *
+ * @author eddie
+ */
+public interface NetworkSocketClosing {
+ /**
+ * Asking parent for permission to close.
+ *
+ * Parent will remove stream from its list and stream shall proceed to close.
+ *
+ * @param key
+ * @return
+ */
+ public boolean canCloseSocket(long key);
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.Socket;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingWorker;
+
+/**
+ *
+ * @author eddie
+ */
+public class NetworkStream extends SwingWorker<Boolean, NetworkMessage> {
+
+ private final Socket _socket;
+
+ static final long longSize = Long.SIZE / 8;
+
+ long _key;
+
+ /**
+ * Thread safe First In, First Out Queue of NetworkMessage objects waiting to be sent.
+ *
+ * Allows the Owner Thread to submit new messages for sending that the Worker Thread
+ * can safely access.
+ */
+ protected ConcurrentLinkedQueue<NetworkMessage> _sendMessageQueue = new ConcurrentLinkedQueue<>();
+
+ private final NetworkMessageEventListenerManager _eventManager;
+
+ private ObjectOutputStream _socketOS;
+
+ private InputStream _socketIS;
+
+ private final NetworkSocketClosing _owner;
+
+ private ObjectInputStream _ois;
+
+ private ObjectOutputStream _oos;
+
+ private ByteArrayOutputStream _baos;
+
+ public NetworkStream(Socket socket, NetworkSocketClosing owner) {
+ this._socket = socket;
+ this._owner = owner;
+ _eventManager = new NetworkMessageEventListenerManager();
+ }
+
+ @Override
+ public Boolean doInBackground() throws Exception {
+
+ _baos = new ByteArrayOutputStream();
+ _oos = new ObjectOutputStream(_baos);
+ _socketOS = new ObjectOutputStream(_socket.getOutputStream());
+
+ _socketIS = _socket.getInputStream();
+ _ois = new ObjectInputStream(_socketIS);
+
+ long expectedLength = longSize;
+ boolean expectingLong = true;
+
+ while (!this.isCancelled()) {
+
+ // check for incoming message
+ if (_ois.available() >= expectedLength) {
+ if (expectingLong == true) {
+ expectedLength = _ois.readLong();
+ System.err.println("Expecting Object with length " + expectedLength);
+ System.err.println("Remaining bytes: " + _ois.available());
+ expectingLong = false;
+ }
+ else {
+ System.err.println("About to read at least " + _ois.available() + " bytes");
+ read();
+ expectedLength = longSize;
+ expectingLong = true;
+ }
+ }
+ // send a queued message
+ NetworkMessage temp = this.sendMessage();
+ if (temp != null) {
+ if (!this.write(temp)) {
+ System.err.println("Unable to send message over TCP channel");
+ // FIXME: Potentially add logger support
+ }
+ }
+
+ }
+ return false;
+ }
+
+ @Override
+ protected void done() {
+ _owner.canCloseSocket(_key);
+ }
+
+ /**
+ * Send a NetworkMessage over a NetworkStream.
+ *
+ * Each message is preceeded by its length as a long to avoid rare, but possible blocking
+ * in the reader.
+ *
+ * @param message
+ * @return true if message successfully sent
+ */
+ public boolean write(NetworkMessage message) {
+ boolean result = false;
+ System.err.println("NetworkStream.write()");
+
+ if (message != null) {
+ try {
+ _oos.writeObject(message);
+ _oos.flush();
+ long messageSize = _baos.size();
+ _socketOS.writeLong(messageSize);
+ System.err.println(" bytes to write: " + messageSize);
+ _baos.writeTo(_socketOS);
+ _socketOS.flush();
+ System.err.println("baos.size()=" + _baos.size());
+
+ result = true;
+
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ // TODO: Implement graceful disconnect
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Reads NetworkMessage from the incoming stream and publishes it.
+ *
+ *
+ * @return true if message successfully published
+ */
+ public boolean read() {
+ boolean result = false;
+ System.err.println("NetworkStream.read()");
+
+ try {
+ NetworkMessage message = null;
+
+ try {
+ message = (NetworkMessage)_ois.readObject();
+ } catch (java.io.OptionalDataException ex) {
+ System.err.println("Length: " + ex.length + " EOF: " + ex.eof);
+ ex.printStackTrace();
+ }
+ message.setKey(_key);
+ publish(message);
+
+ result = true;
+ } catch (IOException ex) {
+ Logger.getLogger(NetworkStream.class.getName()).log(Level.SEVERE, null, ex);
+ // FIXME: Replace logger
+ // TODO: Graceful disconnect
+ } catch (ClassNotFoundException ex) {
+ Logger.getLogger(NetworkStream.class.getName()).log(Level.SEVERE, null, ex);
+ // FIXME: Replace logger
+ }
+ // TODO: Implement NetworkStream::read()
+ return result;
+ }
+
+ /**
+ * Removes a message from the queue of pending messages.
+ *
+ * This method is called on the Worker Thread by the doInBackground() main loop.
+ *
+ * @return a message to be sent
+ */
+ protected NetworkMessage sendMessage() {
+ return this._sendMessageQueue.poll();
+ }
+
+ /* XXX: Methods below here all execute on the GUI Event Dispatch Thread */
+
+ /**
+ * Adds a message to the queue of pending messages.
+ *
+ * This method will usually be called from the Owner Thread.
+ *
+ * @param message to be sent
+ * @return true if the message was added to the queue
+ */
+ public boolean queueMessage(NetworkMessage message) throws IllegalArgumentException {
+ boolean result = false;
+
+ if (message != null) {
+ NetworkMessage temp;
+ try { // make a deep clone of the message
+ temp = NetworkMessage.clone(message);
+ result = this._sendMessageQueue.add(temp);
+ } catch (CloneNotSupportedException e) {
+ // TODO: queueMessage() log CloneNotSupportedException
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
+
+ public NetworkMessageEventListenerManager getEventManager() {
+ return this._eventManager;
+ }
+
+ /**
+ * Fetch messages received over the stream.
+ *
+ * For delivery to event listeners; usually Swing GUI components. This method will run on the
+ * Owner Thread so must complete quickly as that is the GUI Event Dispatch Thread.
+ *
+ * @param list messages received and queued
+ */
+ @Override
+ protected void process(List<NetworkMessage> list) {
+ for (NetworkMessage message: list) {
+ this._eventManager.fireNetworkMessageEvent(message);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * The MIT License
+ *
+ * Copyright 2015 eddie.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package uk.ac.ntu.n0521366.wsyd.libs.net;
+
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ * @author eddie
+ */
+public class NetworkStreamManager implements NetworkSocketClosing {
+
+ public static final long SERVERSOCIAL = -98;
+
+ public static final long SERVERCHAT = -99;
+
+ static final long TEMPCLIENT = -200;
+
+ Random random;
+
+ /**
+ *
+ */
+ public ConcurrentHashMap<Long, NetworkStream> _tcpStreams;
+
+ public NetworkStreamManager() {
+ random = new Random();
+ _tcpStreams = new ConcurrentHashMap<Long, NetworkStream>();
+ }
+
+ public long addStream(long userID, NetworkStream netStream) {
+ long result = 0;
+
+ if (netStream != null) {
+ if (!_tcpStreams.containsKey(userID)) {
+ if (userID == 0) {
+ long tempKey;
+ do {
+ tempKey = createTempKey();
+ } while (_tcpStreams.containsKey(tempKey));
+ _tcpStreams.put(tempKey, netStream);
+ result = tempKey;
+ netStream._key = tempKey;
+ netStream.execute();
+ System.err.println("Added new stream with key: " + tempKey);
+ }
+ else {
+ if ((userID < 0 && userID > TEMPCLIENT) || userID > 0) {
+ _tcpStreams.put(userID, netStream);
+ result = userID;
+ netStream._key = userID;
+ netStream.execute();
+ System.err.println("Added new stream with predefined key: " + userID);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public boolean updateKey(long oldID, long newID) {
+ boolean result = false;
+
+ if (!_tcpStreams.containsKey(newID)) {
+ if (_tcpStreams.containsKey(oldID)) {
+ NetworkStream temp = _tcpStreams.get(oldID);
+ temp._key = newID;
+ _tcpStreams.put(newID, temp);
+ System.err.println("Added new stream with key: " + newID);
+ _tcpStreams.remove(oldID);
+
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ private long createTempKey() {
+ return TEMPCLIENT + -random.nextInt(1000000);
+ }
+
+ @Override
+ public boolean canCloseSocket(long key) {
+ boolean result = false;
+
+ if (_tcpStreams.containsKey(key)) {
+ _tcpStreams.remove(key);
+ result = true;
+ System.err.println("Removed stream with key: " + key);
+ }
+ return result;
+ }
+
+}
throw new IllegalArgumentException(MessageFormat.format("Not a correct dotted decimal notation: {0}", ipv4));
int i = 0;
- for (int index = 0; index <=3; index++) {
+ for (int index = 0; index <= 3; index++) {
try {
i = Integer.parseInt(elements[index]);
} catch (NumberFormatException e) {
import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerUDPMulticast;
import uk.ac.ntu.n0521366.wsyd.libs.net.WSYD_SocketAddress;
import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkMessageEventListener;
+import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerTCP;
import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkServerUDP;
+import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkSocketClosing;
+import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkStream;
+import uk.ac.ntu.n0521366.wsyd.libs.net.NetworkStreamManager;
import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap;
import uk.ac.ntu.n0521366.wsyd.libs.net.ServiceAddressMap.LastSeenHost;
WSYD_SocketAddress _udpControlServiceSA;
NetworkServerUDP _udpControlService;
+
+ WSYD_SocketAddress _tcpListeningServiceSA;
+
+ NetworkServerTCP _tcpListeningService;
+
+ NetworkStreamManager _tcpStreamManager;
/**
+ *
* Default constructor.
*/
public ServerSocial() {
String[] className = this.getClass().getName().split("\\.");
LOGGER = Logger.getLogger(className[className.length - 1]);
LOGGER.setLevel(Level.ALL);
+ if (LOGGER.getParent() != null) {
+ LOGGER.getParent().setLevel(Level.ALL);
+ System.out.println("Parent Logger level " + LOGGER.getParent().getLevel().toString());
+ }
_serviceToAddressMap = new ServiceAddressMap(_title, LOGGER);
readMembers(_membersFile);
_membersOnline = new ArrayList<>();
+ _tcpStreamManager = new NetworkStreamManager();
}
/**
_udpControlService.addNetworkMessageEventListener(this, "Control");
_udpControlService.execute();
+ _tcpListeningServiceSA = new WSYD_SocketAddress(Network.IPv4_WILDCARD, Network.PORTS_SERVER_SOCIAL, WSYD_SocketAddress.Protocol.TCP);
+ _tcpListeningService = new NetworkServerTCP(_tcpListeningServiceSA, _title + "Listener", _serviceToAddressMap, _tcpStreamManager, LOGGER);
+ _tcpListeningService.addNetworkMessageEventListener(this, "ServerSocial");
+ _tcpListeningService.execute();
+
ActionListener servicesAnnounceActionListener = new ActionListener() {
/**
* Activated by timer events to send multi-cast neighbour announcements and other regular notifications.
if (loopCount-- == 0)
ServerSocial.exitRequested = true;
}
- /*try
- {
- _multicastServer.serverClose();
- }
- catch (SocketException e)
- {
- //TODO: Do something
- }*/
+
_servicesAnnounce.stop();
_multicastService.cancel(true);
_udpControlService.cancel(true);