Replaced SocketChannel with Socket.
This commit is contained in:
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -774,7 +774,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1369,7 +1368,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -462,7 +462,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -785,7 +785,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1381,7 +1380,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-8
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -786,7 +786,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1394,7 +1393,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -773,7 +773,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1381,7 +1380,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-8
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -772,7 +772,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1380,7 +1379,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -474,7 +474,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -772,7 +772,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1387,7 +1386,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -474,7 +474,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -773,7 +773,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1408,7 +1407,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -476,7 +476,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -779,7 +779,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1416,7 +1415,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-8
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
@@ -770,7 +770,6 @@ public class Config
|
|||||||
public static boolean PACKET_FLOOD_DROP;
|
public static boolean PACKET_FLOOD_DROP;
|
||||||
public static boolean PACKET_FLOOD_LOGGED;
|
public static boolean PACKET_FLOOD_LOGGED;
|
||||||
public static boolean TCP_NO_DELAY;
|
public static boolean TCP_NO_DELAY;
|
||||||
public static int CONNECTION_TIMEOUT;
|
|
||||||
public static boolean PACKET_ENCRYPTION;
|
public static boolean PACKET_ENCRYPTION;
|
||||||
public static boolean FAILED_DECRYPTION_LOGGED;
|
public static boolean FAILED_DECRYPTION_LOGGED;
|
||||||
public static String DATABASE_DRIVER;
|
public static String DATABASE_DRIVER;
|
||||||
@@ -1407,7 +1406,6 @@ public class Config
|
|||||||
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
PACKET_FLOOD_DROP = serverConfig.getBoolean("PacketFloodDrop", false);
|
||||||
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
PACKET_FLOOD_LOGGED = serverConfig.getBoolean("PacketFloodLogged", true);
|
||||||
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
TCP_NO_DELAY = serverConfig.getBoolean("TcpNoDelay", true);
|
||||||
CONNECTION_TIMEOUT = serverConfig.getInt("ConnectionTimeout", 800);
|
|
||||||
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
PACKET_ENCRYPTION = serverConfig.getBoolean("PacketEncryption", false);
|
||||||
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
FAILED_DECRYPTION_LOGGED = serverConfig.getBoolean("FailedDecryptionLogged", true);
|
||||||
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
REQUEST_ID = serverConfig.getInt("RequestServerID", 0);
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
// Iterate client pool.
|
// Iterate client pool.
|
||||||
ITERATE: for (E client : _pool)
|
ITERATE: for (E client : _pool)
|
||||||
{
|
{
|
||||||
if (client.getChannel() == null)
|
if (client.getSocket() == null)
|
||||||
{
|
{
|
||||||
_pool.remove(client);
|
_pool.remove(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -72,16 +68,12 @@ public class ExecuteThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -15,27 +16,30 @@ public class NetClient
|
|||||||
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(NetClient.class.getName());
|
||||||
|
|
||||||
private String _ip;
|
private String _ip;
|
||||||
private SocketChannel _channel;
|
private Socket _socket;
|
||||||
|
private InputStream _inputStream;
|
||||||
|
private OutputStream _outputStream;
|
||||||
private NetConfig _netConfig;
|
private NetConfig _netConfig;
|
||||||
private Queue<byte[]> _pendingPacketData;
|
private Queue<byte[]> _packetData;
|
||||||
private ByteBuffer _pendingByteBuffer;
|
private byte[] _pendingData;
|
||||||
private int _pendingPacketSize;
|
private int _pendingPacketSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the client.
|
* Initialize the client.
|
||||||
* @param channel
|
* @param socket
|
||||||
* @param netConfig
|
* @param netConfig
|
||||||
*/
|
*/
|
||||||
public void init(SocketChannel channel, NetConfig netConfig)
|
public void init(Socket socket, NetConfig netConfig)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_socket = socket;
|
||||||
_netConfig = netConfig;
|
_netConfig = netConfig;
|
||||||
_pendingPacketData = new ConcurrentLinkedQueue<>();
|
_packetData = new ConcurrentLinkedQueue<>();
|
||||||
|
_ip = socket.getInetAddress().toString().substring(1); // Trim out /127.0.0.1
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_ip = _channel.getRemoteAddress().toString();
|
_inputStream = _socket.getInputStream();
|
||||||
_ip = _ip.substring(1, _ip.lastIndexOf(':')); // Trim out /127.0.0.1:12345
|
_outputStream = _socket.getOutputStream();
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
@@ -64,26 +68,28 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if (_channel != null)
|
if (_socket != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_channel.close();
|
_socket.close();
|
||||||
_channel = null;
|
_socket = null;
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingPacketData != null)
|
if (_packetData != null)
|
||||||
{
|
{
|
||||||
_pendingPacketData.clear();
|
_packetData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingByteBuffer != null)
|
if (_pendingData != null)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = null;
|
_pendingData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@ public class NetClient
|
|||||||
public void addPacketData(byte[] data)
|
public void addPacketData(byte[] data)
|
||||||
{
|
{
|
||||||
// Check packet flooding.
|
// Check packet flooding.
|
||||||
final int size = _pendingPacketData.size();
|
final int size = _packetData.size();
|
||||||
if (size >= _netConfig.getPacketQueueLimit())
|
if (size >= _netConfig.getPacketQueueLimit())
|
||||||
{
|
{
|
||||||
if (_netConfig.isPacketFloodDisconnect())
|
if (_netConfig.isPacketFloodDisconnect())
|
||||||
@@ -121,7 +127,7 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue.
|
// Add to queue.
|
||||||
_pendingPacketData.add(data);
|
_packetData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,24 +135,24 @@ public class NetClient
|
|||||||
*/
|
*/
|
||||||
public Queue<byte[]> getPacketData()
|
public Queue<byte[]> getPacketData()
|
||||||
{
|
{
|
||||||
return _pendingPacketData;
|
return _packetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the pending read ByteBuffer.
|
* @return the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getPendingByteBuffer()
|
public byte[] getPendingData()
|
||||||
{
|
{
|
||||||
return _pendingByteBuffer;
|
return _pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the pending read ByteBuffer.
|
* Set the pending read <b>byte[]</b>.
|
||||||
* @param pendingByteBuffer the pending read ByteBuffer.
|
* @param pendingData the pending read <b>byte[]</b>.
|
||||||
*/
|
*/
|
||||||
public void setPendingByteBuffer(ByteBuffer pendingByteBuffer)
|
public void setPendingData(byte[] pendingData)
|
||||||
{
|
{
|
||||||
_pendingByteBuffer = pendingByteBuffer;
|
_pendingData = pendingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +172,36 @@ public class NetClient
|
|||||||
_pendingPacketSize = pendingPacketSize;
|
_pendingPacketSize = pendingPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet over the network using the default encryption.
|
||||||
|
* @param packet The packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(WritablePacket packet)
|
||||||
|
{
|
||||||
|
if ((_socket == null) || !_socket.isConnected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sendableBytes = packet.getSendableBytes(getEncryption());
|
||||||
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
_outputStream.write(sendableBytes);
|
||||||
|
_outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Encryption of this client.
|
* @return the Encryption of this client.
|
||||||
*/
|
*/
|
||||||
@@ -175,11 +211,27 @@ public class NetClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SocketChannel of this client.
|
* @return the Socket of this client.
|
||||||
*/
|
*/
|
||||||
public SocketChannel getChannel()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the InputStream of this client.
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OutputStream of this client.
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream()
|
||||||
|
{
|
||||||
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class NetConfig
|
|||||||
{
|
{
|
||||||
private int _readPoolSize = 100;
|
private int _readPoolSize = 100;
|
||||||
private int _executePoolSize = 50;
|
private int _executePoolSize = 50;
|
||||||
private int _connectionTimeout = 800;
|
|
||||||
private int _packetQueueLimit = 80;
|
private int _packetQueueLimit = 80;
|
||||||
private boolean _packetFloodDisconnect = false;
|
private boolean _packetFloodDisconnect = false;
|
||||||
private boolean _packetFloodDrop = false;
|
private boolean _packetFloodDrop = false;
|
||||||
@@ -50,23 +49,6 @@ public class NetConfig
|
|||||||
_executePoolSize = executePoolSize;
|
_executePoolSize = executePoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the timeout until a connection is established.
|
|
||||||
*/
|
|
||||||
public int getConnectionTimeout()
|
|
||||||
{
|
|
||||||
return _connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout until a connection is established.
|
|
||||||
* @param connectionTimeout
|
|
||||||
*/
|
|
||||||
public void setConnectionTimeout(int connectionTimeout)
|
|
||||||
{
|
|
||||||
_connectionTimeout = connectionTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the packet queue limit of receivable packets.
|
* @return the packet queue limit of receivable packets.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.ServerSocketChannel;
|
import java.net.ServerSocket;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.net.Socket;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -105,30 +105,26 @@ public class NetServer<E extends NetClient>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// Create server and bind port.
|
// Create server and bind port.
|
||||||
try (ServerSocketChannel server = ServerSocketChannel.open())
|
try (ServerSocket server = new ServerSocket())
|
||||||
{
|
{
|
||||||
|
server.setReuseAddress(true);
|
||||||
server.bind(new InetSocketAddress(_hostname, _port));
|
server.bind(new InetSocketAddress(_hostname, _port));
|
||||||
server.configureBlocking(false); // Non-blocking I/O.
|
server.setSoTimeout(0); // Non-blocking I/O.
|
||||||
|
|
||||||
// Listen for new connections.
|
// Listen for new connections.
|
||||||
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
LOGGER.info(_name + ": Listening on port " + _port + " for incoming connections.");
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
final Socket socket = server.accept();
|
||||||
|
if (socket != null)
|
||||||
final SocketChannel channel = server.accept();
|
|
||||||
if (channel != null)
|
|
||||||
{
|
{
|
||||||
// Configure channel.
|
// Configure channel.
|
||||||
channel.socket().setTcpNoDelay(_netConfig.isTcpNoDelay());
|
socket.setTcpNoDelay(_netConfig.isTcpNoDelay());
|
||||||
channel.socket().setSoTimeout(_netConfig.getConnectionTimeout());
|
socket.setSoTimeout(0); // Non-blocking I/O.
|
||||||
channel.configureBlocking(false); // Non-blocking I/O.
|
|
||||||
|
|
||||||
// Create client.
|
// Create client.
|
||||||
final E client = _clientSupplier.get();
|
final E client = _clientSupplier.get();
|
||||||
client.init(channel, _netConfig);
|
client.init(socket, _netConfig);
|
||||||
|
|
||||||
// Add to read pool.
|
// Add to read pool.
|
||||||
|
|
||||||
@@ -190,11 +186,7 @@ public class NetServer<E extends NetClient>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly polling the channel.
|
// Prevent high CPU caused by repeatedly polling the channel.
|
||||||
currentTime = System.currentTimeMillis();
|
Thread.sleep(1);
|
||||||
if ((currentTime - executionStart) < 1)
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +11,9 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class ReadThread<E extends NetClient> implements Runnable
|
public class ReadThread<E extends NetClient> implements Runnable
|
||||||
{
|
{
|
||||||
private final ByteBuffer _sizeBuffer = ByteBuffer.allocate(2); // Reusable size buffer.
|
|
||||||
private final ByteBuffer _pendingSizeBuffer = ByteBuffer.allocate(1); // Reusable pending size buffer.
|
|
||||||
private final Set<E> _pool;
|
private final Set<E> _pool;
|
||||||
|
private final byte[] _sizeBuffer = new byte[2]; // Reusable size buffer.
|
||||||
|
private final byte[] _pendingSizeBuffer = new byte[1]; // Reusable pending size buffer.
|
||||||
|
|
||||||
public ReadThread(Set<E> pool)
|
public ReadThread(Set<E> pool)
|
||||||
{
|
{
|
||||||
@@ -24,12 +23,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long executionStart;
|
|
||||||
long currentTime;
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
executionStart = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// No need to iterate when pool is empty.
|
// No need to iterate when pool is empty.
|
||||||
if (!_pool.isEmpty())
|
if (!_pool.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -38,22 +33,29 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final SocketChannel channel = client.getChannel();
|
final InputStream inputStream = client.getInputStream();
|
||||||
if (channel == null) // Unexpected disconnection?
|
if (inputStream == null) // Unexpected disconnection?
|
||||||
{
|
{
|
||||||
// Null SocketChannel: client
|
// Null InputStream: client
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue read if there is a pending ByteBuffer.
|
// Continue when there are no bytes that can be read.
|
||||||
final ByteBuffer pendingByteBuffer = client.getPendingByteBuffer();
|
if (inputStream.available() < 1)
|
||||||
if (pendingByteBuffer != null)
|
|
||||||
{
|
{
|
||||||
// Allocate an additional ByteBuffer based on pending packet size.
|
continue ITERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue read if there is a pending byte array.
|
||||||
|
final byte[] pendingData = client.getPendingData();
|
||||||
|
if (pendingData != null)
|
||||||
|
{
|
||||||
|
// Allocate an additional byte array based on pending packet size.
|
||||||
final int pendingPacketSize = client.getPendingPacketSize();
|
final int pendingPacketSize = client.getPendingPacketSize();
|
||||||
final ByteBuffer additionalData = ByteBuffer.allocate(pendingPacketSize - pendingByteBuffer.position());
|
final byte[] additionalData = new byte[pendingPacketSize - pendingData.length];
|
||||||
switch (channel.read(additionalData))
|
final int bytesRead = inputStream.read(additionalData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -70,15 +72,21 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Merge additional read data.
|
// Merge additional read data.
|
||||||
pendingByteBuffer.put(pendingByteBuffer.position(), additionalData, 0, additionalData.position());
|
final int currentSize = pendingData.length + bytesRead;
|
||||||
pendingByteBuffer.position(pendingByteBuffer.position() + additionalData.position());
|
final byte[] mergedData = new byte[currentSize];
|
||||||
|
System.arraycopy(pendingData, 0, mergedData, 0, pendingData.length);
|
||||||
|
System.arraycopy(additionalData, 0, mergedData, pendingData.length, bytesRead);
|
||||||
|
|
||||||
// Read was complete.
|
// Read was complete.
|
||||||
if (pendingByteBuffer.position() >= pendingPacketSize)
|
if (currentSize >= pendingPacketSize)
|
||||||
{
|
{
|
||||||
// Add packet data to client.
|
// Add packet data to client.
|
||||||
client.addPacketData(pendingByteBuffer.array());
|
client.addPacketData(mergedData);
|
||||||
client.setPendingByteBuffer(null);
|
client.setPendingData(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.setPendingData(mergedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +94,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read incoming packet size (short).
|
// Read incoming packet size (short).
|
||||||
_sizeBuffer.clear();
|
boolean sizeRead = false;
|
||||||
switch (channel.read(_sizeBuffer))
|
switch (inputStream.read(_sizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -103,8 +111,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Need to read two bytes to calculate size.
|
// Need to read two bytes to calculate size.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
int attempt = 0; // Keep it under 10 attempts (100ms).
|
// Keep it under 10 attempts (100ms).
|
||||||
COMPLETE_SIZE_READ: while ((attempt++ < 10) && (_sizeBuffer.position() < 2))
|
COMPLETE_SIZE_READ: for (int attempt = 0; attempt < 10; attempt++)
|
||||||
{
|
{
|
||||||
// Wait for pending data.
|
// Wait for pending data.
|
||||||
try
|
try
|
||||||
@@ -116,8 +124,8 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the missing extra byte.
|
// Try to read the missing extra byte.
|
||||||
_pendingSizeBuffer.clear();
|
_pendingSizeBuffer[0] = 0;
|
||||||
switch (channel.read(_pendingSizeBuffer))
|
switch (inputStream.read(_pendingSizeBuffer))
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -133,14 +141,15 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Merge additional read byte.
|
// Merge additional read byte.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
_sizeBuffer.put(1, _pendingSizeBuffer, 0, 1);
|
_sizeBuffer[1] = _pendingSizeBuffer[0];
|
||||||
_sizeBuffer.position(2);
|
sizeRead = true;
|
||||||
|
break COMPLETE_SIZE_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read failed.
|
// Read failed.
|
||||||
if (_sizeBuffer.position() < 2)
|
if (!sizeRead)
|
||||||
{
|
{
|
||||||
onDisconnection(client);
|
onDisconnection(client);
|
||||||
continue ITERATE;
|
continue ITERATE;
|
||||||
@@ -151,10 +160,11 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
// Read actual packet bytes.
|
// Read actual packet bytes.
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Allocate a new ByteBuffer based on packet size read.
|
// Allocate a new byte array based on packet size read.
|
||||||
final int packetSize = calculatePacketSize();
|
final int packetSize = calculatePacketSize();
|
||||||
final ByteBuffer packetByteBuffer = ByteBuffer.allocate(packetSize);
|
final byte[] packetData = new byte[packetSize];
|
||||||
switch (channel.read(packetByteBuffer))
|
final int bytesRead = inputStream.read(packetData);
|
||||||
|
switch (bytesRead)
|
||||||
{
|
{
|
||||||
// Disconnected.
|
// Disconnected.
|
||||||
case -1:
|
case -1:
|
||||||
@@ -171,14 +181,16 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Read was not complete.
|
// Read was not complete.
|
||||||
if (packetByteBuffer.position() < packetSize)
|
if (bytesRead < packetSize)
|
||||||
{
|
{
|
||||||
client.setPendingByteBuffer(packetByteBuffer);
|
final byte[] pendindBytes = new byte[bytesRead];
|
||||||
|
System.arraycopy(packetData, 0, pendindBytes, 0, bytesRead);
|
||||||
|
client.setPendingData(pendindBytes);
|
||||||
client.setPendingPacketSize(packetSize);
|
client.setPendingPacketSize(packetSize);
|
||||||
}
|
}
|
||||||
else // Add packet data to client.
|
else // Add packet data to client.
|
||||||
{
|
{
|
||||||
client.addPacketData(packetByteBuffer.array());
|
client.addPacketData(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,24 +209,19 @@ public class ReadThread<E extends NetClient> implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent high CPU caused by repeatedly looping.
|
// Prevent high CPU caused by repeatedly looping.
|
||||||
currentTime = System.currentTimeMillis();
|
try
|
||||||
if ((currentTime - executionStart) < 1)
|
{
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculatePacketSize()
|
private int calculatePacketSize()
|
||||||
{
|
{
|
||||||
_sizeBuffer.rewind();
|
return ((_sizeBuffer[0] & 0xff) | ((_sizeBuffer[1] << 8) & 0xffff)) - 2;
|
||||||
return ((_sizeBuffer.get() & 0xff) | ((_sizeBuffer.get() << 8) & 0xffff)) - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisconnection(E client)
|
private void onDisconnection(E client)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.l2jmobius.commons.network;
|
package org.l2jmobius.commons.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -13,7 +12,6 @@ public abstract class WritablePacket
|
|||||||
{
|
{
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private byte[] _sendableBytes;
|
private byte[] _sendableBytes;
|
||||||
private ByteBuffer _byteBuffer;
|
|
||||||
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
private int _position = 2; // Allocate space for size (max length 65535 - size header).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,38 +268,6 @@ public abstract class WritablePacket
|
|||||||
return _sendableBytes;
|
return _sendableBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public ByteBuffer getSendableByteBuffer()
|
|
||||||
{
|
|
||||||
return getSendableByteBuffer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryption if EncryptionInterface is used.
|
|
||||||
* @return ByteBuffer of the sendable packet data, including a size header.
|
|
||||||
*/
|
|
||||||
public synchronized ByteBuffer getSendableByteBuffer(EncryptionInterface encryption)
|
|
||||||
{
|
|
||||||
// Generate sendable ByteBuffer.
|
|
||||||
if ((_byteBuffer == null /* Not processed */) || (encryption != null /* Encryption can change */))
|
|
||||||
{
|
|
||||||
final byte[] bytes = getSendableBytes(encryption);
|
|
||||||
if (bytes != null) // Data was actually written.
|
|
||||||
{
|
|
||||||
_byteBuffer = ByteBuffer.wrap(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Rewind the buffer.
|
|
||||||
{
|
|
||||||
_byteBuffer.rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the buffer.
|
|
||||||
return _byteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take in consideration that data must be written first.
|
* Take in consideration that data must be written first.
|
||||||
* @return The length of the data (includes size header).
|
* @return The length of the data (includes size header).
|
||||||
|
|||||||
@@ -486,7 +486,6 @@ public class GameServer
|
|||||||
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
server.getNetConfig().setPacketFloodDrop(Config.PACKET_FLOOD_DROP);
|
||||||
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
server.getNetConfig().setPacketFloodLogged(Config.PACKET_FLOOD_LOGGED);
|
||||||
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
server.getNetConfig().setTcpNoDelay(Config.TCP_NO_DELAY);
|
||||||
server.getNetConfig().setConnectionTimeout(Config.CONNECTION_TIMEOUT);
|
|
||||||
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
server.getNetConfig().setFailedDecryptionLogged(Config.FAILED_DECRYPTION_LOGGED);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.gameserver.network;
|
package org.l2jmobius.gameserver.network;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -224,30 +222,11 @@ public class GameClient extends NetClient
|
|||||||
_pendingPackets.add(packet);
|
_pendingPackets.add(packet);
|
||||||
synchronized (_pendingPackets)
|
synchronized (_pendingPackets)
|
||||||
{
|
{
|
||||||
final SocketChannel channel = getChannel();
|
// Send the packet data.
|
||||||
if ((channel != null) && channel.isConnected())
|
super.sendPacket(packet);
|
||||||
{
|
|
||||||
final ServerPacket sendPacket = _pendingPackets.poll();
|
|
||||||
final ByteBuffer byteBuffer = sendPacket.getSendableByteBuffer(_encryption);
|
|
||||||
if (byteBuffer != null)
|
|
||||||
{
|
|
||||||
// Send the packet data.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Loop while there are remaining bytes in the buffer.
|
|
||||||
while (byteBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
channel.write(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run packet implementation.
|
// Run packet implementation.
|
||||||
sendPacket.run(_player);
|
packet.run(_player);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.l2jmobius.loginserver.network;
|
package org.l2jmobius.loginserver.network;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -145,20 +147,26 @@ public class LoginClient extends NetClient
|
|||||||
return _connectionStartTime;
|
return _connectionStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void sendPacket(WritablePacket packet)
|
public void sendPacket(WritablePacket packet)
|
||||||
{
|
{
|
||||||
if ((packet == null))
|
final Socket socket = getSocket();
|
||||||
|
if ((socket != null) && socket.isConnected())
|
||||||
{
|
{
|
||||||
return;
|
final byte[] sendableBytes = packet.getSendableBytes();
|
||||||
}
|
if (sendableBytes == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Write into the channel.
|
|
||||||
if ((getChannel() != null) && getChannel().isConnected())
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Send the packet data.
|
final OutputStream outputStream = getOutputStream();
|
||||||
getChannel().write(packet.getSendableByteBuffer());
|
synchronized (this)
|
||||||
|
{
|
||||||
|
outputStream.write(sendableBytes);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ PacketFloodLogged = True
|
|||||||
# Default: True (disabled)
|
# Default: True (disabled)
|
||||||
TcpNoDelay = True
|
TcpNoDelay = True
|
||||||
|
|
||||||
# Connection timeout in milliseconds.
|
|
||||||
# Default 800
|
|
||||||
ConnectionTimeout = 800
|
|
||||||
|
|
||||||
# Packet encryption.
|
# Packet encryption.
|
||||||
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
# By default packets sent or received are encrypted using the Blowfish algorithm.
|
||||||
# Disabling this reduces the resources needed to process any packets transfered,
|
# Disabling this reduces the resources needed to process any packets transfered,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user