/*******************************************************************************
* Copyright 2015 Klaus Pfeiffer <klaus@allpiper.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.jfastnet;
import com.esotericsoftware.kryo.Kryo;
import com.jfastnet.config.SerialiserConfig;
import com.jfastnet.idprovider.ClientIdReliableModeIdProvider;
import com.jfastnet.idprovider.IIdProvider;
import com.jfastnet.peers.CongestionControl;
import com.jfastnet.peers.javanet.JavaNetPeer;
import com.jfastnet.processors.*;
import com.jfastnet.serialiser.ISerialiser;
import com.jfastnet.serialiser.KryoSerialiser;
import com.jfastnet.time.ITimeProvider;
import com.jfastnet.time.SystemTimeProvider;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/** Configure JFastNet with this configuration class. We don't care much about
* visibility here, because it's only used for configuration of the system and
* access to the fields look much cleaner without the setter/getter boilerplate.
*
* @author Klaus Pfeiffer - klaus@allpiper.com */
@Setter
@Getter
@Accessors(chain = true)
public class Config {
/** Message receiver that will simply call process on the message. */
public static final IMessageReceiver<Void> DEFAULT_MESSAGE_RECEIVER = message -> message.process(null);
public static final List<Class> DEFAULT_MESSAGE_PROCESSORS = new ArrayList<>();
static {
// add default processors in the order in which they get called
DEFAULT_MESSAGE_PROCESSORS.add(AddChecksumProcessor.class);
DEFAULT_MESSAGE_PROCESSORS.add(DiscardWrongChecksumMessagesHandler.class);
DEFAULT_MESSAGE_PROCESSORS.add(MessageLogProcessor.class);
DEFAULT_MESSAGE_PROCESSORS.add(StackedMessageProcessor.class);
DEFAULT_MESSAGE_PROCESSORS.add(ReliableModeAckProcessor.class);
DEFAULT_MESSAGE_PROCESSORS.add(ReliableModeSequenceProcessor.class);
DEFAULT_MESSAGE_PROCESSORS.add(DiscardMessagesHandler.class);
}
/** Map for additional configuration (e.g. for the processors). */
public Map<Class, Object> additionalConfigMap = new HashMap<>();
{
setAdditionalConfig(new StackedMessageProcessor.ProcessorConfig());
setAdditionalConfig(new ReliableModeAckProcessor.ProcessorConfig());
setAdditionalConfig(new ReliableModeSequenceProcessor.ProcessorConfig());
setAdditionalConfig(new MessageLogProcessor.ProcessorConfig());
setAdditionalConfig(new CongestionControl.CongestionControlConfig());
}
public Object context;
/** Hostname or IP address. */
public String host = "127.0.0.1";
/** Port number to accept or request new UDP connections on. */
public int port = 0;
/** On client this can be 0 so a free port is automatically used. */
public int bindPort = 0;
/** Optional configured sender id. 0 is reserved for the server. */
public int senderId;
/** Consumer is called when a new client id is retrieved through the
* ConnectResponse message. */
public Consumer<Integer> newSenderIdConsumer = id -> {};
/** UDP peer system to use. (e.g. KryoNetty) */
public Class<? extends IPeer> udpPeerClass = JavaNetPeer.class;
/** Set to true if you want that a CSV file is created after every run with
* data about all the sent and received messages and their data size. */
public boolean trackData = false;
/** Collected data. Only used if trackData is set to true. */
public NetStats netStats = new NetStats();
/** Used for the timestamp for new messages. */
public ITimeProvider timeProvider = new SystemTimeProvider();
/** Provides the message ids. */
public Class<? extends IIdProvider> idProviderClass = ClientIdReliableModeIdProvider.class;
/** JFastNet internal message sender. Don't change. */
public IMessageSender internalSender;
/** JFastNet internal message receiver for received messages. Don't change. */
public IMessageReceiver internalReceiver;
/** Configure an external receiver for incoming messages. Must be thread-safe. */
public IMessageReceiver externalReceiver = DEFAULT_MESSAGE_RECEIVER;
/** Serialisation system. Some peers require specific serialisation
* return types. */
public ISerialiser serialiser = new KryoSerialiser(new SerialiserConfig(), new Kryo());
/** Compress MessagePart messages. */
public boolean compressBigMessages = false;
/** Required for the reliable sequence mode. Interval in ms. */
public int keepAliveInterval = 3000;
/** If keepalive messages can be stacked. */
public boolean stackKeepAliveMessages = false;
/** Time in ms when peer considers other side as not reachable. */
public int timeoutThreshold = keepAliveInterval * 6; //2;
// BEGIN server config
/** All client ids that are expected to join. */
public List<Integer> expectedClientIds = new ArrayList<>();
/** Map of clients that are required to connect.
* Key: client id, value: true if connected. */
public Map<Integer, Boolean> requiredClients = new ConcurrentHashMap<>();
/** Time that has to be passed to consider a received connect request as
* new. */
public int timeSinceLastConnectRequest = 3000;
/** Called by the server. */
public IServerHooks serverHooks = new IServerHooks() {};
// END server config
// BEGIN client config
/** Time in ms the client tries to connect to the server. */
public int connectTimeout = 5000;
// END client config
/** Packets above this size will log an error or will be automatically
* splitted into multiple messages. */
public int maximumUdpPacketSize = 1024;
/** Automatically split too big messages into multiple smaller messages. */
public boolean autoSplitTooBigMessages = true;
public int messageQueueThreshold = 37;
//SocketOption send buffer SO_SNDBUF
public int socketSendBufferSize = 131072; //65536;
public int socketReceiveBufferSize = 65536;
public int receiveBufferAllocator = 65536;
/** Maximum size of event log queue. */
public int eventLogSize = 4096;
/** Delay in ms between sending of queued messages. */
public int queuedMessagesDelay = 50;
/** List of all added processors. */
public List<Class> processorClasses = DEFAULT_MESSAGE_PROCESSORS;
public <E> void setAdditionalConfig(E config) {
additionalConfigMap.put(config.getClass(), config);
}
public <E> E getAdditionalConfig(Class<E> configClass) {
return (E) additionalConfigMap.get(configClass);
}
public final Debug debug = new Debug();
@Setter
@Getter
@Accessors(chain = true)
public static class Debug {
private final Random debugRandom = new Random();
/** Set to true if you want to simulate packet loss or an otherwise
* rough environment. */
public boolean enabled = false;
/** Set to true if you want to simulate packet loss for the next
* received packet. */
public boolean discardNextPacket = false;
/** Specify percentage of lost packages from 0 to 100 where 100 means
* every packet. */
public int lostPacketsPercentage = 1;
public boolean simulateLossOfPacket() {
if (discardNextPacket) {
discardNextPacket = false;
return true;
}
return enabled && debugRandom.nextInt(100) < lostPacketsPercentage;
}
}
}