/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package staticContent.evaluation.simulator.core.networkComponent;
import staticContent.evaluation.simulator.Simulator;
import staticContent.evaluation.simulator.annotations.plugin.PluginSuperclass;
import staticContent.evaluation.simulator.core.event.ClientEvent;
import staticContent.evaluation.simulator.core.event.DistantProxyEvent;
import staticContent.evaluation.simulator.core.event.Event;
import staticContent.evaluation.simulator.core.event.MixEvent;
import staticContent.evaluation.simulator.core.message.EndToEndMessage;
import staticContent.evaluation.simulator.core.message.MixMessage;
import staticContent.evaluation.simulator.core.message.NetworkMessage;
import staticContent.evaluation.simulator.core.message.TransportMessage;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType;
import userGeneratedContent.simulatorPlugIns.plugins.clientSendStyle.ClientSendStyleImpl;
@PluginSuperclass( layerName = "Load Generator", layerKey = "TYPE_OF_TRAFFIC_GENERATOR", position = 0)
public abstract class AbstractClient extends NetworkNode {
protected Simulator simulator;
protected ClientSendStyleImpl clientSendStyle;
private int messageCreationTime;
private int messageDecryptionTime;
protected int clientId;
private static int idCounter = 0;
protected final boolean simulateReplyChannel;
protected final boolean closedLoopSending;
public int latest;
public AbstractClient(String identifier, Simulator simulator) {
super(identifier, simulator);
this.simulator = simulator;
this.messageCreationTime = Simulator.settings.getPropertyAsInt("MIX_REQUEST_CREATION_TIME"); // in ms
this.messageDecryptionTime = Simulator.settings.getPropertyAsInt("MIX_REPLY_DECRYPTION_TIME"); // in ms
this.clientId = idCounter++;
this.simulateReplyChannel = Simulator.settings.getProperty("COMMUNICATION_MODE").equals("SIMPLEX_REPLY") || Simulator.settings.getProperty("COMMUNICATION_MODE").equals("DUPLEX");
this.closedLoopSending = Simulator.settings.getProperty("COMMUNICATION_MODE").equals("SIMPLEX_WITH_FEEDBACK");
}
/**
* called when a message (reply from a server to a message previously sent
* with the method sendMessage(EndToEndMessage message)) has reached this
* client.
* @param message
*/
public abstract void incomingMessage(EndToEndMessage message);
/**
* called when a message (that was previously sent with the method
* sendMessage(EndToEndMessage message)) has reached the server simulator.
* can for example be used to assert that messages aren't replayed too fast
* (i.e. before previous messages are transmitted) in simplex mode
* @param message
*/
public abstract void messageReachedServer(EndToEndMessage message);
public abstract void close();
/**
* subclasses may use this method to send messages to a server (messages
* will be routed via mixes if mixes are specified in the simulation
* script).
* @param message
*/
public TransportMessage sendMessage(EndToEndMessage message) {
TransportMessage toSend = new TransportMessage(true, this, simulator.getDistantProxy(), Simulator.getNow(), this, message.getPayload().getRequestSize(), message);
Simulator.trafficSourceStatistics.increment(1, StatisticsType.AVG_TRAFFICSOURCE_SENDING_RATE_PER_CLIENT);
statistics.addValue(toSend.getLength(), StatisticsType.ADU_SIZE_SEND);
statistics.addValue(toSend.getLength(), StatisticsType.ADU_SIZE_SENDANDRECEIVE);
statistics.addValue(toSend.getLength(), StatisticsType.CF_ADU_SIZE_SENDANDRECEIVE);
statistics.increment(toSend.getLength(), StatisticsType.CF_AVG_THROUGHPUT_PER_CLIENT_SEND);
statistics.increment(toSend.getLength(), StatisticsType.CF_AVG_THROUGHPUT_PER_CLIENT_SENDANDRECEIVE);
if (Simulator.settings.getProperty("COMMUNICATION_MODE").equals("SIMPLEX_REPLY")) {
simulator.getDistantProxy().incomingRequest(toSend);
} else {
clientSendStyle.incomingRequestFromUser(toSend);
}
return toSend;
}
private MixMessage generateMixRequest() {
return MixMessage.getInstance(true, this, simulator.getDistantProxy(), this, Simulator.getNow(), false);
}
private int getMixRequestEncryptionTime() {
return messageCreationTime;
}
private int getMixReplyDecryptionTime() {
return messageDecryptionTime;
}
@Override
public void executeEvent(Event event) {
if (event.getEventType() instanceof MixEvent && event.getEventType() == MixEvent.INCOMING_REPLY_FROM_DISTANT_PROXY) {
incomingDecryptedReply((NetworkMessage)event.getAttachment());
} else if (!(event.getEventType() instanceof ClientEvent)) {
throw new RuntimeException("ERROR: " +super.getIdentifier() +" received wrong Event: " +event.toString());
} else {
switch ((ClientEvent)event.getEventType()) {
case SEND_MIX_REQUEST:
sendToNextHop(generateMixRequest(), getMixRequestEncryptionTime(), MixEvent.INCOMING_MIX_MESSAGE_OF_TYPE_REQUEST);
break;
case REQUEST_FROM_USER:
EndToEndMessage etem = (EndToEndMessage)event.getAttachment();
sendMessage(etem);
break;
case REPLY_FROM_MIX:
//System.out.println("in1: " +event.getAttachment());
event.reuse(this, Simulator.getNow() + getMixReplyDecryptionTime(), ClientEvent.REPLY_DECRYPTED);
simulator.scheduleEvent(event, this);
break;
case REPLY_DECRYPTED:
//System.out.println("in2: " +event.getAttachment());
incomingDecryptedReply((NetworkMessage)event.getAttachment());
break;
/*case INVITATION_TO_SEND_NEXT_MIX_MESSAGE:
((ClientBasicSynchronous)clientCommunicationBehaviour).executeEvent(event);
break;*/
default:
throw new RuntimeException("ERROR: " +super.getIdentifier() +" received unknown Event: " +event.toString());
}
}
}
private void incomingDecryptedReply(NetworkMessage networkMessage) {
if (networkMessage instanceof TransportMessage) {
TransportMessage tm = (TransportMessage)networkMessage;
statistics.addValue(Simulator.getNow() - tm.getCreationTime(), StatisticsType.AVG_CLIENT_RTT_LAYER5MESSAGE);
statistics.addValue(tm.getLength(), StatisticsType.ADU_SIZE_RECEIVE);
statistics.addValue(tm.getLength(), StatisticsType.ADU_SIZE_SENDANDRECEIVE);
statistics.addValue(tm.getLength(), StatisticsType.CF_ADU_SIZE_SENDANDRECEIVE);
incomingMessage(tm.reltedEndToEndMessage);
clientSendStyle.incomingDecryptedReply(tm);
} else if (networkMessage instanceof MixMessage) {
MixMessage mixMessage = (MixMessage)networkMessage;
statistics.addValue(Simulator.getNow() - mixMessage.getCreationTime(), StatisticsType.AVG_CLIENT_LATENCY_REPLYMIXMESSAGE);
if (!mixMessage.isDummy())
for (TransportMessage tm: mixMessage.getTransportMessagesContained()) {
statistics.addValue(tm.getLength(), StatisticsType.ADU_SIZE_RECEIVE);
statistics.addValue(tm.getLength(), StatisticsType.ADU_SIZE_SENDANDRECEIVE);
statistics.addValue(tm.getLength(), StatisticsType.CF_ADU_SIZE_SENDANDRECEIVE);
statistics.addValue(Simulator.getNow() - tm.getCreationTime(), StatisticsType.AVG_CLIENT_RTT_LAYER5MESSAGE);
statistics.increment(tm.getLength(), StatisticsType.CF_AVG_THROUGHPUT_PER_CLIENT_RECEIVE);
statistics.increment(tm.getLength(), StatisticsType.CF_AVG_THROUGHPUT_PER_CLIENT_SENDANDRECEIVE);
incomingMessage(tm.reltedEndToEndMessage);
}
clientSendStyle.incomingDecryptedReply(mixMessage);
} else
throw new RuntimeException("ERROR: unknown MESSAGE_FORMAT! " +networkMessage);
}
// called by associated ClientCommunicationBehaviour (hides implementation-details)
public void sendRequest(NetworkMessage networkMessage) {
if (networkMessage instanceof MixMessage) {
Simulator.trafficSourceStatistics.increment(1, StatisticsType.AVG_MIXMESSAGE_SENDING_RATE_PER_CLIENT);
sendToNextHop(networkMessage, getMixRequestEncryptionTime(), MixEvent.INCOMING_MIX_MESSAGE_OF_TYPE_REQUEST);
} else if (networkMessage instanceof TransportMessage) {
sendToNextHop(networkMessage, getMixRequestEncryptionTime(), DistantProxyEvent.INCOMING_REQUEST);
} else
throw new RuntimeException("ERROR: Client does not support messages of type " +networkMessage +"!");
}
public int getClientId() {
return clientId;
}
public static void reset() {
idCounter = 0;
}
public void setSendStyle(ClientSendStyleImpl clientSendStyle) {
if (this.clientSendStyle != null)
throw new RuntimeException("ERROR: this method may only be envoked once!");
this.clientSendStyle = clientSendStyle;
}
}