added possibility to use multiple bytes as separator
This commit is contained in:
parent
1dcf9dec7a
commit
63df33457f
@ -5,7 +5,7 @@ plugins {
|
||||
}
|
||||
|
||||
group 'me.bvn13'
|
||||
version '1.2.2'
|
||||
version '1.2.4'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -63,6 +63,7 @@ public abstract class AbstractClientListener implements Runnable {
|
||||
|
||||
/**
|
||||
* Reads line (separated with '\n') from socket
|
||||
*
|
||||
* @return the line read from socket
|
||||
*/
|
||||
public String readLine() {
|
||||
@ -78,17 +79,32 @@ public abstract class AbstractClientListener implements Runnable {
|
||||
|
||||
/**
|
||||
* Reads data from socket until {@code separator} is encountered
|
||||
*
|
||||
* @param separator byte to separate data portions
|
||||
* @return array of bytes read from socket
|
||||
*/
|
||||
public byte[] readBytes(byte separator) {
|
||||
final List<Byte> data = new ArrayList<>(256);
|
||||
public byte[] readBytes(byte[] separator) {
|
||||
final List<Byte> data = new ArrayList<>(2048 * 2048);
|
||||
List<Byte> buffer = new ArrayList<>(separator.length);
|
||||
int separatorPosition = 0;
|
||||
try {
|
||||
while (socket.isConnected()) {
|
||||
byte[] portion = in.readNBytes(1);
|
||||
if (portion == null || portion.length == 0 || portion[0] == separator) {
|
||||
if (portion == null || portion.length == 0) {
|
||||
break;
|
||||
}
|
||||
if (portion[0] == separator[separatorPosition]) {
|
||||
if (separatorPosition == separator.length - 1) {
|
||||
break;
|
||||
}
|
||||
separatorPosition++;
|
||||
buffer.add(portion[0]);
|
||||
continue;
|
||||
} else {
|
||||
separatorPosition = 0;
|
||||
data.addAll(buffer);
|
||||
buffer.clear();
|
||||
}
|
||||
data.add(portion[0]);
|
||||
}
|
||||
final byte[] bytes = new byte[data.size()];
|
||||
@ -96,7 +112,7 @@ public abstract class AbstractClientListener implements Runnable {
|
||||
for (Byte aByte : data) {
|
||||
bytes[i++] = aByte;
|
||||
}
|
||||
if (log.isTraceEnabled()) log.trace("Received: " + new String(bytes));
|
||||
if (log.isTraceEnabled()) log.trace("Received {} bytes: {}", bytes.length, bytes);
|
||||
return bytes;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -106,11 +122,12 @@ public abstract class AbstractClientListener implements Runnable {
|
||||
/**
|
||||
* Writes line into socket ending with default separator '\n'.
|
||||
* Flushes after writing.
|
||||
*
|
||||
* @param bytes bytes to be sent into socket
|
||||
* @param separator byte to separate data portions
|
||||
*/
|
||||
public void writeBytes(byte[] bytes, byte separator) {
|
||||
if (log.isTraceEnabled()) log.trace("Sending: " + new String(bytes));
|
||||
public void writeBytes(byte[] bytes, byte[] separator) {
|
||||
if (log.isTraceEnabled()) log.trace("Sending {} bytes: {}", bytes.length, bytes);
|
||||
try {
|
||||
out.write(bytes);
|
||||
out.write(separator);
|
||||
@ -123,6 +140,7 @@ public abstract class AbstractClientListener implements Runnable {
|
||||
/**
|
||||
* Writes line into socket ending with default separator '\n'.
|
||||
* Flushes after writing.
|
||||
*
|
||||
* @param data data to be sent into socket
|
||||
*/
|
||||
public void writeLine(String data) {
|
||||
|
@ -89,9 +89,13 @@ public class Client<T extends AbstractClientListener> {
|
||||
*/
|
||||
public void stop() {
|
||||
log.debug("Stopping client");
|
||||
if (client != null) {
|
||||
client.stop();
|
||||
}
|
||||
try {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to close socket");
|
||||
}
|
||||
@ -119,7 +123,7 @@ public class Client<T extends AbstractClientListener> {
|
||||
* @param separator
|
||||
* @return
|
||||
*/
|
||||
public byte[] readBytes(byte separator) {
|
||||
public byte[] readBytes(byte[] separator) {
|
||||
return client.readBytes(separator);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class CommandClientListener extends AbstractClientListener implements Abs
|
||||
log.error("Unexpected command received");
|
||||
continue;
|
||||
}
|
||||
log.debug("Command received: " + command);
|
||||
log.debug("Command received: " + command.getClass());
|
||||
if (!(command instanceof AbstractCommand)) {
|
||||
log.warn("Incorrect command received: " + command);
|
||||
continue;
|
||||
|
@ -99,7 +99,9 @@ public class Server<T extends AbstractClientListener> {
|
||||
iterator.remove();
|
||||
}
|
||||
try {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to close socket");
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public final class Sewy {
|
||||
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||
|
||||
private final List<Class<?>> registeredDataTypes = new CopyOnWriteArrayList<>();
|
||||
private byte separator = SEPARATOR;
|
||||
private byte[] separator = new byte[] { SEPARATOR };
|
||||
|
||||
/**
|
||||
* Registers command in white list for further communications
|
||||
@ -55,11 +55,11 @@ public final class Sewy {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte getSeparator() {
|
||||
public static byte[] getSeparator() {
|
||||
return getInstance().separator;
|
||||
}
|
||||
|
||||
public static void setSeparator(byte separator) {
|
||||
public static void setSeparator(byte[] separator) {
|
||||
try {
|
||||
LOCK.lock();
|
||||
getInstance().separator = separator;
|
||||
|
@ -16,6 +16,7 @@
|
||||
package me.bvn13.sewy;
|
||||
|
||||
import me.bvn13.sewy.command.AbstractCommand;
|
||||
import me.bvn13.sewy.command.ComplexCommand;
|
||||
import me.bvn13.sewy.command.PingCommand;
|
||||
import me.bvn13.sewy.command.PongCommand;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@ -23,6 +24,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ServerTest {
|
||||
|
||||
@ -112,4 +114,43 @@ public class ServerTest {
|
||||
Assertions.assertTrue(latency.get() > 0);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = START_PORT + 7)
|
||||
void wideSeparatorTest(int port) throws InterruptedException {
|
||||
Sewy.register(ComplexCommand.class);
|
||||
Sewy.setSeparator(new byte[] { '\n', 'e', 'n', 'd', '\n' });
|
||||
|
||||
AtomicReference<ComplexCommand> check = new AtomicReference<>();
|
||||
|
||||
CommandServer server = new CommandServer("localhost", port, (socket) -> new CommandClientListener(socket) {
|
||||
@Override
|
||||
public AbstractCommand onCommand(AbstractCommand command) {
|
||||
if (command instanceof ComplexCommand) {
|
||||
check.set((ComplexCommand) command);
|
||||
return null;
|
||||
}
|
||||
throw new IllegalArgumentException(command.toString());
|
||||
}
|
||||
});
|
||||
|
||||
CommandClient client = new CommandClient("localhost", port, (socket) -> new CommandClientListener(socket) {
|
||||
@Override
|
||||
public AbstractCommand onCommand(AbstractCommand command) {
|
||||
throw new IllegalArgumentException(command.toString());
|
||||
}
|
||||
});
|
||||
|
||||
ComplexCommand command = new ComplexCommand();
|
||||
command.add(new ComplexCommand.SimpleData("a1"));
|
||||
command.add(new ComplexCommand.SimpleData("b2"));
|
||||
command.add(new ComplexCommand.SimpleData("finish"));
|
||||
|
||||
client.send(command);
|
||||
Thread.sleep(1000);
|
||||
Assertions.assertNotNull(check.get());
|
||||
Assertions.assertEquals(3, check.get().getDatum().size());
|
||||
Assertions.assertEquals("a1", check.get().getDatum().get(0).getString());
|
||||
Assertions.assertEquals("b2", check.get().getDatum().get(1).getString());
|
||||
Assertions.assertEquals("finish", check.get().getDatum().get(2).getString());
|
||||
}
|
||||
}
|
||||
|
44
src/test/java/me/bvn13/sewy/command/ComplexCommand.java
Normal file
44
src/test/java/me/bvn13/sewy/command/ComplexCommand.java
Normal file
@ -0,0 +1,44 @@
|
||||
package me.bvn13.sewy.command;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ComplexCommand extends AbstractCommand {
|
||||
|
||||
private List<SimpleData> datum;
|
||||
|
||||
public ComplexCommand() {
|
||||
datum = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ComplexCommand(List<SimpleData> datum) {
|
||||
this.datum = datum;
|
||||
}
|
||||
|
||||
public void add(SimpleData data) {
|
||||
datum.add(data);
|
||||
}
|
||||
|
||||
public ComplexCommand setDatum(List<SimpleData> datum) {
|
||||
this.datum = datum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<SimpleData> getDatum() {
|
||||
return datum;
|
||||
}
|
||||
|
||||
public static class SimpleData implements Serializable {
|
||||
private final String string;
|
||||
|
||||
public SimpleData(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user