From 892ebd3c86e1b40bc8414b2c12fcda8d59ff1d46 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 4 Jan 2016 15:44:15 -0600 Subject: [PATCH] Improved UDP code to improve performance and reduce bandwidth --- src/com/projectswg/ClientConnection.java | 15 ++ src/com/projectswg/ClientReceiver.java | 13 +- src/com/projectswg/ClientSender.java | 114 +++++++---- src/com/projectswg/Connections.java | 153 ++++++++++++++ src/com/projectswg/Forwarder.java | 187 +++++++++++------- .../projectswg/networking/soe/Fragmented.java | 16 +- 6 files changed, 373 insertions(+), 125 deletions(-) create mode 100644 src/com/projectswg/Connections.java diff --git a/src/com/projectswg/ClientConnection.java b/src/com/projectswg/ClientConnection.java index d06c731..3b0b88d 100644 --- a/src/com/projectswg/ClientConnection.java +++ b/src/com/projectswg/ClientConnection.java @@ -23,6 +23,7 @@ public class ClientConnection { interceptor = new NetInterceptor(); sender = new ClientSender(interceptor, loginPort, zonePort); receiver = new ClientReceiver(interceptor); + sender.setClientReceiver(receiver); receiver.setClientSender(sender); } @@ -34,10 +35,12 @@ public class ClientConnection { sender.setZoneCallback((packet) -> receiver.onPacket(true, packet)); receiver.setReceiverCallback(new ClientReceiverCallback() { public void onPacket(byte[] data) { ClientConnection.this.onPacket(data); } + public void onUdpRecv(boolean zone, byte[] data) { ClientConnection.this.onUdpRecv(zone, data); } public void onDisconnected() { ClientConnection.this.onDisconnected(); } public void onConnected() { ClientConnection.this.onConnected(); } public void onConnectionChanged(ConnectionState state) { ClientConnection.this.onConnectionStateChanged(state); } }); + sender.setSenderCallback((zone, data) -> onUdpSent(zone, data)); pinger = Executors.newSingleThreadScheduledExecutor(); pinger.scheduleAtFixedRate(()->ping(), 0, 1000, TimeUnit.MILLISECONDS); } @@ -63,6 +66,16 @@ public class ClientConnection { callback.onPacket(data); } + private void onUdpSent(boolean zone, byte [] data) { + if (callback != null) + callback.onUdpSent(zone, data); + } + + private void onUdpRecv(boolean zone, byte [] data) { + if (callback != null) + callback.onUdpRecv(zone, data); + } + private void onDisconnected() { connected = false; if (callback != null) @@ -105,6 +118,8 @@ public class ClientConnection { void onConnected(); void onDisconnected(); void onPacket(byte [] data); + void onUdpSent(boolean zone, byte [] data); + void onUdpRecv(boolean zone, byte [] data); } } diff --git a/src/com/projectswg/ClientReceiver.java b/src/com/projectswg/ClientReceiver.java index 3157ea8..89c95bc 100644 --- a/src/com/projectswg/ClientReceiver.java +++ b/src/com/projectswg/ClientReceiver.java @@ -78,6 +78,8 @@ public class ClientReceiver { if (zone && packet.getData().length == 4) { // Ping sender.sendRaw(packet.getPort(), packet.getAddress(), packet.getData()); lastPacket.set(System.nanoTime()); + if (callback != null) + callback.onUdpRecv(zone, packet.getData()); return; } ByteBuffer data = ByteBuffer.wrap(packet.getData()).order(ByteOrder.BIG_ENDIAN); @@ -89,12 +91,17 @@ public class ClientReceiver { sender.setPort(packet.getPort()); lastPacket.set(System.nanoTime()); process(data); + if (callback != null) + callback.onUdpRecv(zone, data.array()); } else { if (packet.getPort() != port || port == 0) return; lastPacket.set(System.nanoTime()); executor.submit(() -> { - process(ByteBuffer.wrap(Encryption.decode(data.array(), 0)).order(ByteOrder.BIG_ENDIAN)); + ByteBuffer decoded = ByteBuffer.wrap(Encryption.decode(data.array(), 0)).order(ByteOrder.BIG_ENDIAN); + process(decoded); + if (callback != null) + callback.onUdpRecv(zone, decoded.array()); }); } } @@ -203,10 +210,11 @@ public class ClientReceiver { } private void onOutOfOrder(OutOfOrder ooo) { - + System.out.println("OOO " + ooo.getSequence()); } private void onAcknowledge(Acknowledge ack) { + System.out.println("ACK " + ack.getSequence()); sender.onAcknowledge(ack.getSequence()); } @@ -221,6 +229,7 @@ public class ClientReceiver { void onDisconnected(); void onConnectionChanged(ConnectionState state); void onPacket(byte [] data); + void onUdpRecv(boolean zone, byte [] data); } public enum ConnectionState { diff --git a/src/com/projectswg/ClientSender.java b/src/com/projectswg/ClientSender.java index bcb28d2..654e7bb 100644 --- a/src/com/projectswg/ClientSender.java +++ b/src/com/projectswg/ClientSender.java @@ -25,7 +25,6 @@ import com.projectswg.networking.soe.Disconnect.DisconnectReason; public class ClientSender { private static final InetAddress ADDR = InetAddress.getLoopbackAddress(); - private static final int MAX_PACKET_SIZE = 496; private final NetInterceptor interceptor; private UDPServer loginServer; @@ -33,6 +32,8 @@ public class ClientSender { private Queue sentPackets; private Queue inboundQueue; private ExecutorService executor; + private ClientSenderCallback callback; + private ClientReceiver receiver; private short txSequence; private int connectionId; private int port; @@ -73,6 +74,10 @@ public class ClientSender { zoneServer.close(); } + public void setClientReceiver(ClientReceiver receiver) { + this.receiver = receiver; + } + public void setLoginCallback(UDPCallback callback) { loginServer.setCallback(callback); } @@ -81,6 +86,10 @@ public class ClientSender { zoneServer.setCallback(callback); } + public void setSenderCallback(ClientSenderCallback callback) { + this.callback = callback; + } + public int getZonePort() { return zoneServer.getPort(); } @@ -147,6 +156,8 @@ public class ClientSender { zoneServer.send(port, addr, data); else loginServer.send(port, addr, data); + if (callback != null) + callback.onUdpSent(zone, data); } public void send(Packet packet) { @@ -167,49 +178,24 @@ public class ClientSender { private void outboundRunnable() { final InetAddress addr = InetAddress.getLoopbackAddress(); + double time = 0; + long lastBurst = 0; + double timeSinceBurst = 0; while (true) { - synchronized (sentPackets) { - int sent = 0; - for (SequencedOutbound packet : sentPackets) { - sendRaw(port, addr, packet.getData()); - Thread.yield(); - if (++sent >= 500) - break; - } - } - try { - Thread.sleep(50); - } catch (InterruptedException e) { - break; - } - } - } - - private void inboundRunnable() { - byte [] data; - Queue outbound = new ArrayBlockingQueue<>(32, true); - int size = 0; - while (true) { - size = 6; - synchronized (inboundQueue) { - while (!inboundQueue.isEmpty()) { - data = interceptor.interceptServer(inboundQueue.poll()); - if ((size + data.length > MAX_PACKET_SIZE-7 && !outbound.isEmpty()) || outbound.size() == 32) { - createOutboundPacket(outbound, size); - size = 6; + time = receiver.getTimeSinceLastPacket(); + timeSinceBurst = (System.nanoTime() - lastBurst) / 1E6; + if (time <= timeSinceBurst || (time >= 100 && timeSinceBurst >= 500)) { + synchronized (sentPackets) { + int sent = 0; + for (SequencedOutbound packet : sentPackets) { + sendRaw(port, addr, packet.getData()); + Thread.yield(); + if (++sent >= 500) + break; } - outbound.add(data); - size += data.length + 1 + (data.length >= 0xFF ? 2 : 0); - if (size > MAX_PACKET_SIZE-4) { - createOutboundPacket(outbound, size); - size = 6; - } - Thread.yield(); } + lastBurst = System.nanoTime(); } - if (!outbound.isEmpty()) - createOutboundPacket(outbound, size); - size = 0; try { Thread.sleep(25); } catch (InterruptedException e) { @@ -218,8 +204,48 @@ public class ClientSender { } } + private void inboundRunnable() { + final int dataHeaderSize = 8; + byte [] data; + Queue outbound = new ArrayBlockingQueue<>(8, true); + int size = 0; + while (true) { + size = dataHeaderSize; + synchronized (inboundQueue) { + while (!inboundQueue.isEmpty()) { + data = interceptor.interceptServer(inboundQueue.poll()); + if ((size + getPacketLength(data) >= 496 && !outbound.isEmpty()) || outbound.size() == 8) { + createOutboundPacket(outbound, size); + size = dataHeaderSize; + } + outbound.add(data); + size += getPacketLength(data); + if (size >= 496) { + createOutboundPacket(outbound, size); + size = dataHeaderSize; + } + Thread.yield(); + } + } + if (!outbound.isEmpty()) + createOutboundPacket(outbound, size); + try { + Thread.sleep(25); + } catch (InterruptedException e) { + break; + } + } + } + + private int getPacketLength(byte [] data) { + if (data.length >= 255) + return data.length + 3; + else + return data.length + 1; + } + private void createOutboundPacket(Queue outbound, int size) { - if (size <= MAX_PACKET_SIZE-4) { // Fits into single data packet + if (size <= 496) { // Fits into single data packet DataChannelA channel = new DataChannelA(); channel.setSequence(txSequence++); while (!outbound.isEmpty()) @@ -227,7 +253,7 @@ public class ClientSender { send(channel); } else { // Fragmented while (!outbound.isEmpty()) { - Fragmented [] frags = Fragmented.encode(ByteBuffer.wrap(outbound.poll()), txSequence, MAX_PACKET_SIZE); + Fragmented [] frags = Fragmented.encode(ByteBuffer.wrap(outbound.poll()), txSequence); txSequence += frags.length; for (Fragmented frag : frags) { send(frag); @@ -236,6 +262,10 @@ public class ClientSender { } } + public interface ClientSenderCallback { + void onUdpSent(boolean zone, byte [] data); + } + private static class SequencedOutbound implements SequencedPacket { private short sequence; diff --git a/src/com/projectswg/Connections.java b/src/com/projectswg/Connections.java new file mode 100644 index 0000000..a55ac66 --- /dev/null +++ b/src/com/projectswg/Connections.java @@ -0,0 +1,153 @@ +package com.projectswg; + +import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicLong; + +import com.projectswg.ClientConnection.ClientCallback; +import com.projectswg.ServerConnection.ServerCallback; + +public class Connections { + + private final ServerConnection server; + private final ClientConnection client; + private final AtomicLong tcpRecv; + private final AtomicLong tcpSent; + private final AtomicLong udpRecv; + private final AtomicLong udpSent; + private ConnectionCallback callback; + private InetAddress addr; + private int port; + + public Connections() { + server = new ServerConnection(); + client = new ClientConnection(44453, 44463); + tcpRecv = new AtomicLong(0); + tcpSent = new AtomicLong(0); + udpRecv = new AtomicLong(0); + udpSent = new AtomicLong(0); + addr = ServerConnection.DEFAULT_ADDR; + port = ServerConnection.DEFAULT_PORT; + callback = null; + setCallbacks(); + } + + public void initialize() { + client.start(); + } + + public void terminate() { + server.stop(); + client.stop(); + } + + public void setCallback(ConnectionCallback callback) { + this.callback = callback; + } + + public void setRemote(InetAddress addr, int port) { + if (this.addr.equals(addr) && this.port == port) + return; + terminate(); + server.setRemoteAddress(addr, port); + initialize(); + } + + public InetAddress getAddress() { + return addr; + } + + public int getPort() { + return port; + } + + public long getTcpRecv() { + return tcpRecv.get(); + } + + public long getTcpSent() { + return tcpSent.get(); + } + + public long getUdpRecv() { + return udpRecv.get(); + } + + public long getUdpSent() { + return udpSent.get(); + } + + private void setCallbacks() { + client.setCallback(new ClientCallback() { + public void onPacket(byte[] data) { onDataSentTcp(data); } + public void onUdpSent(boolean zone, byte[] data) { onDataSentUdp(data); } + public void onUdpRecv(boolean zone, byte[] data) { onDataRecvUdp(data); } + public void onDisconnected() { onClientDisconnected(); } + public void onConnected() { onClientConnected(); } + }); + server.setCallback(new ServerCallback() { + public void onData(byte[] data) { onDataRecvTcp(data); } + public void onConnected() { onServerConnected(); } + public void onDisconnected() { onServerDisconnected(); } + }); + } + + private void onServerConnected() { + if (callback != null) + callback.onServerConnected(); + } + + private void onServerDisconnected() { + if (callback != null) + callback.onServerDisconnected(); + } + + private void onClientConnected() { + server.start(); + if (callback != null) + callback.onClientConnected(); + } + + private void onClientDisconnected() { + server.stop(); + if (callback != null) + callback.onClientDisconnected(); + } + + private void onDataRecvTcp(byte [] data) { + tcpRecv.addAndGet(data.length); + client.send(data); + if (callback != null) + callback.onDataRecvTcp(data); + } + + private void onDataSentTcp(byte [] data) { + tcpSent.addAndGet(data.length); + server.forward(data); + if (callback != null) + callback.onDataSentTcp(data); + } + + private void onDataRecvUdp(byte [] data) { + udpRecv.addAndGet(data.length); + if (callback != null) + callback.onDataRecvUdp(data); + } + + private void onDataSentUdp(byte [] data) { + udpSent.addAndGet(data.length); + if (callback != null) + callback.onDataSentUdp(data); + } + + public interface ConnectionCallback { + void onServerConnected(); + void onServerDisconnected(); + void onClientConnected(); + void onClientDisconnected(); + void onDataRecvTcp(byte [] data); + void onDataSentTcp(byte [] data); + void onDataRecvUdp(byte [] data); + void onDataSentUdp(byte [] data); + } + +} diff --git a/src/com/projectswg/Forwarder.java b/src/com/projectswg/Forwarder.java index 865c97e..e1350f7 100644 --- a/src/com/projectswg/Forwarder.java +++ b/src/com/projectswg/Forwarder.java @@ -2,145 +2,186 @@ package com.projectswg; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.concurrent.atomic.AtomicLong; import javafx.application.Application; import javafx.application.Platform; +import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; +import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; +import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; +import javafx.scene.paint.Color; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.stage.WindowEvent; -import com.projectswg.ClientConnection.ClientCallback; -import com.projectswg.ServerConnection.ServerCallback; +import com.projectswg.Connections.ConnectionCallback; -public class Forwarder extends Application { +public class Forwarder extends Application implements ConnectionCallback { private static final String [] DATA_NAMES = new String[]{"B", "KB", "MB", "GB", "TB"}; - private static final String SERVER_CONN_STRING = "Server Connection: %s"; - private static final String CLIENT_CONN_STRING = "Client Connection: %s"; - private static final String SERVER_RX_STRING = "TCP Recv: %s"; - private static final String SERVER_TX_STRING = "TCP Sent: %s"; - private final ServerConnection server; - private final ClientConnection client; - private final AtomicLong receivedBytes; - private final AtomicLong transmittedBytes; + private final Connections connections; private final TextField serverIpField; private final TextField serverPortField; + private final Button serverSetButton; private final Text serverConnectionText; private final Text clientConnectionText; - private final Text serverReceivedText; - private final Text serverTransmittedText; + private final Text serverRxText; + private final Text serverTxText; + private final Text clientRxText; + private final Text clientTxText; public static void main(String [] args) { launch(args); } public Forwarder() { - receivedBytes = new AtomicLong(0); - transmittedBytes = new AtomicLong(0); + connections = new Connections(); serverIpField = new TextField(ServerConnection.DEFAULT_ADDR.getHostAddress()); serverPortField = new TextField(Integer.toString(ServerConnection.DEFAULT_PORT)); - serverConnectionText = new Text(String.format(SERVER_CONN_STRING, getConnectionStatus(false))); - clientConnectionText = new Text(String.format(CLIENT_CONN_STRING, getConnectionStatus(false))); - serverReceivedText = new Text(String.format(SERVER_RX_STRING, getByteName(receivedBytes.get()))); - serverTransmittedText = new Text(String.format(SERVER_TX_STRING, getByteName(transmittedBytes.get()))); + serverSetButton = new Button("Set"); + serverConnectionText = new Text(getConnectionStatus(false)); + clientConnectionText = new Text(getConnectionStatus(false)); + serverRxText = new Text(getByteName(connections.getTcpRecv())); + serverTxText = new Text(getByteName(connections.getTcpSent())); + clientRxText = new Text(getByteName(connections.getUdpRecv())); + clientTxText = new Text(getByteName(connections.getUdpSent())); + updateConnection(serverConnectionText, false); + updateConnection(clientConnectionText, false); + updateServerButton(); EventHandler handler = new EventHandler() { - @Override public void handle(KeyEvent event) { if (event.getCode() == KeyCode.ENTER) updateServerIp(); + else + updateServerButton(); } }; serverIpField.setOnKeyPressed(handler); serverPortField.setOnKeyPressed(handler); - server = new ServerConnection(); - client = new ClientConnection(44453, 44463); - client.setCallback(new ClientCallback() { - @Override - public void onPacket(byte[] data) { - transmittedBytes.addAndGet(data.length); - server.forward(data); - Platform.runLater(() -> serverTransmittedText.setText(String.format(SERVER_TX_STRING, getByteName(transmittedBytes.get())))); - } - @Override - public void onDisconnected() { - Platform.runLater(() -> clientConnectionText.setText(String.format(CLIENT_CONN_STRING, getConnectionStatus(false)))); - server.stop(); - } - @Override - public void onConnected() { - Platform.runLater(() -> clientConnectionText.setText(String.format(CLIENT_CONN_STRING, getConnectionStatus(true)))); - server.start(); - } - }); - server.setCallback(new ServerCallback() { - @Override - public void onData(byte[] data) { - receivedBytes.addAndGet(data.length); - client.send(data); - Platform.runLater(() -> serverReceivedText.setText(String.format(SERVER_RX_STRING, getByteName(receivedBytes.get())))); - } - @Override - public void onConnected() { - Platform.runLater(() -> serverConnectionText.setText(String.format(SERVER_CONN_STRING, getConnectionStatus(true)))); - } - @Override - public void onDisconnected() { - Platform.runLater(() -> serverConnectionText.setText(String.format(SERVER_CONN_STRING, getConnectionStatus(false)))); + serverIpField.setOnKeyTyped(handler); + serverPortField.setOnKeyTyped(handler); + serverSetButton.setOnAction(new EventHandler(){ + public void handle(ActionEvent event) { + updateServerIp(); } }); + connections.setCallback(this); } - private void initialize() { - client.start(); + @Override + public void onServerConnected() { + Platform.runLater(() -> updateConnection(serverConnectionText, true)); } - private void terminate() { - server.stop(); - client.stop(); + @Override + public void onServerDisconnected() { + Platform.runLater(() -> updateConnection(serverConnectionText, false)); + } + + @Override + public void onClientConnected() { + Platform.runLater(() -> updateConnection(clientConnectionText, true)); + } + + @Override + public void onClientDisconnected() { + Platform.runLater(() -> updateConnection(clientConnectionText, false)); + } + + @Override + public void onDataRecvTcp(byte[] data) { + Platform.runLater(() -> serverRxText.setText(getByteName(connections.getTcpRecv()))); + } + + @Override + public void onDataSentTcp(byte[] data) { + Platform.runLater(() -> serverTxText.setText(getByteName(connections.getTcpSent()))); + } + + @Override + public void onDataRecvUdp(byte[] data) { + Platform.runLater(() -> clientRxText.setText(getByteName(connections.getUdpRecv()))); + } + + @Override + public void onDataSentUdp(byte[] data) { + Platform.runLater(() -> clientTxText.setText(getByteName(connections.getUdpSent()))); + } + + private void updateConnection(Text t, boolean status) { + t.setText(getConnectionStatus(status)); + t.setFill(status ? Color.GREEN : Color.RED); } private void updateServerIp() { try { InetAddress addr = InetAddress.getByName(serverIpField.getText()); int port = Integer.parseInt(serverPortField.getText()); - terminate(); - server.setRemoteAddress(addr, port); - initialize(); + connections.setRemote(addr, port); + updateServerButton(); } catch (UnknownHostException e) { - e.printStackTrace(); + System.err.println("Unknown IP: " + serverIpField.getText()); + } catch (NumberFormatException e) { + System.err.println("Invalid Port: " + serverPortField.getText()); + } + } + + private void updateServerButton() { + try { + InetAddress addr = InetAddress.getByName(serverIpField.getText()); + int port = Integer.parseInt(serverPortField.getText()); + if (!addr.equals(connections.getAddress()) || port != connections.getPort()) + serverSetButton.setDisable(false); + else + serverSetButton.setDisable(true); + } catch (UnknownHostException | NumberFormatException e) { + serverSetButton.setDisable(true); } } @Override public void start(Stage primaryStage) throws Exception { - initialize(); + connections.initialize(); GridPane root = new GridPane(); - root.add(serverIpField, 0, 0); - root.add(serverPortField, 1, 0); - root.add(serverConnectionText, 0, 1); - root.add(clientConnectionText, 0, 2); - root.add(serverReceivedText, 0, 3); - root.add(serverTransmittedText, 0, 4); + for (int i = 0; i < 4; i++) { + ColumnConstraints cc = new ColumnConstraints(); + cc.setPercentWidth(25); + root.getColumnConstraints().add(cc); + } + root.add(serverIpField, 0, 0, 2, 1); + root.add(serverPortField, 2, 0, 1, 1); + root.add(serverSetButton, 3, 0, 1, 1); + root.add(new Text("Server Connection:"), 0, 1, 2, 1); + root.add(serverConnectionText, 2, 1, 2, 1); + root.add(new Text("Client Connection:"), 0, 2, 2, 1); + root.add(clientConnectionText, 2, 2, 2, 1); + root.add(new Text("Sent"), 1, 3, 1, 1); + root.add(new Text("Recv"), 2, 3, 1, 1); + root.add(new Text("TCP"), 0, 4, 1, 1); + root.add(serverTxText, 1, 4, 1, 1); + root.add(serverRxText, 2, 4, 1, 1); + root.add(new Text("UDP"), 0, 5, 1, 1); + root.add(clientTxText, 1, 5, 1, 1); + root.add(clientRxText, 2, 5, 1, 1); Scene scene = new Scene(root, 300, 160); - scene.setRoot(root); primaryStage.setTitle("Holocore Forwarder"); primaryStage.setScene(scene); primaryStage.setMinWidth(scene.getWidth()); primaryStage.setMinHeight(scene.getHeight()); primaryStage.setMaxWidth(scene.getWidth()); primaryStage.setMaxHeight(scene.getHeight()); + root.setOnMouseClicked((event) -> root.requestFocus()); + root.requestFocus(); primaryStage.show(); primaryStage.setOnCloseRequest(new EventHandler() { public void handle(WindowEvent we) { - terminate(); + connections.terminate(); primaryStage.close(); } }); diff --git a/src/com/projectswg/networking/soe/Fragmented.java b/src/com/projectswg/networking/soe/Fragmented.java index 0f8a0d7..b234137 100644 --- a/src/com/projectswg/networking/soe/Fragmented.java +++ b/src/com/projectswg/networking/soe/Fragmented.java @@ -71,12 +71,12 @@ public class Fragmented extends Packet implements SequencedPacket { return data; } - public Fragmented [] encode(int startSequence, int maxSize) { + public Fragmented [] encode(int startSequence) { packet.position(0); int ord = 0; - Fragmented [] packets = new Fragmented[(int) Math.ceil((packet.remaining()+4)/(maxSize-7.0))]; + Fragmented [] packets = new Fragmented[(int) Math.ceil((packet.remaining()+4)/489.0)]; while (packet.remaining() > 0) { - packets[ord] = createSegment(startSequence++, ord++, maxSize, packet); + packets[ord] = createSegment(startSequence++, ord++, packet); } return packets; } @@ -108,19 +108,19 @@ public class Fragmented extends Packet implements SequencedPacket { public short getSequence() { return sequence; } public int getDatLength() { return length; } - public static final Fragmented [] encode(ByteBuffer data, int startSequence, int maxSize) { + public static final Fragmented [] encode(ByteBuffer data, int startSequence) { data.position(0); int ord = 0; - Fragmented [] packets = new Fragmented[(int) Math.ceil((data.remaining()+4)/(maxSize-7.0))]; + Fragmented [] packets = new Fragmented[(int) Math.ceil((data.remaining()+4)/489.0)]; while (data.remaining() > 0) { - packets[ord] = createSegment(startSequence++, ord++, maxSize, data); + packets[ord] = createSegment(startSequence++, ord++, data); } return packets; } - private static final Fragmented createSegment(int startSequence, int ord, int maxSize, ByteBuffer packet) { + private static final Fragmented createSegment(int startSequence, int ord, ByteBuffer packet) { int header = (ord == 0) ? 8 : 4; - ByteBuffer data = ByteBuffer.allocate(Math.min(packet.remaining()+header, maxSize-3)); + ByteBuffer data = ByteBuffer.allocate(Math.min(packet.remaining()+header, 493)); addNetShort(data, 0x0D); addNetShort(data, startSequence);