Протокол SCTP является очень перспективным и предоставляет множество интересных возможностей. В данной заметке я рассмотрю использование продвинутых SCTP клиента и сервера из примеров netty (простые клиент и сервер рассмотрены тут:
Простые SCTP клиент и сервер ). Проект будет на maven.
Скорее всего с ходу вам скомпилировать примеры из репозитория (
репозиторий) не удастся. Я перечислю еще раз все установки и изменения, после которых пример должен заработать (возможно некоторые шаги не обязательны).
Исключение вида
java.lang.UnsupportedOperationException: libsctp.so.1: cannot open shared object file: No such file or directory лечится установкой библиотеки:
Исключения
java.net.SocketException: Permission denied при запуске лечатся установкой переменной окружения:
export _JAVA_OPTIONS="-Djava.net.preferIPv4Stack=true" |
export _JAVA_OPTIONS="-Djava.net.preferIPv4Stack=true"
и/или отключением SELinux (
https://bugs.openjdk.java.net/browse/JDK-7045222 ) - у меня установлен CentOS :
echo 0 > /selinux/enforce |
echo 0 > /selinux/enforce
Исключения вида
com.sun.nio.sctp. UnsupportedOperatingSystemException.raise (UnsupportedOperatingSystemException.java:20 означают, что для запуска использована не та java. Нужна java из JDK, она подхватывает необходимые классы из com.sun.nio.sctp.*.
Без этих зависимостей я собрать не мог:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.jboss.errai.io.netty</groupId>
<artifactId>netty-transport-sctp</artifactId>
<version>4.0.0.Alpha1.errai.r1</version>
</dependency> |
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.jboss.errai.io.netty</groupId>
<artifactId>netty-transport-sctp</artifactId>
<version>4.0.0.Alpha1.errai.r1</version>
</dependency>
Клиент и сервер из примеров занимаются пересылкой друг другу одной и той же строки. Я изменил эту логику: клиент присылает строку, сервер делает квитирование. При этом строки извлечены из SCTP сообщения в буфер и готовы для дальнейшей обработки (у меня они просто выводятся в стандартный поток). Таким образом, изменения коснулись только хендлеров.
Я не буду приводить весь исходный код и загромождать статью. Приведу только измененные места ( исходники можно скачать тут:
sctp_advanced).
Класс SctpEchoClientHandler. В конструкторе урезал размер буфера и пишу в него только читаемые символы.
public SctpEchoClientHandler() {
firstMessage = Unpooled.buffer(SctpEchoClient.SIZE);
for (int i = 49; i < 127/*firstMessage.capacity()*/; i++) {
firstMessage.writeByte((byte) i);
}
} |
public SctpEchoClientHandler() {
firstMessage = Unpooled.buffer(SctpEchoClient.SIZE);
for (int i = 49; i < 127/*firstMessage.capacity()*/; i++) {
firstMessage.writeByte((byte) i);
}
}
А тут сделано эхоподавление 🙂 (оно просто закомментировано). Клиент выводит в поток ответ сервера на принятое сообщение (можно поместить содержимое буфера в строку и передать на дальнейшую обработку):
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// got response from server, preparing String representation
SctpMessage sm = (SctpMessage) msg;
ByteBuf buffer = sm.content(); //
System.out.println("Response from server received: ");
for (int i = 0; i < buffer.capacity(); i ++) {
byte b = buffer.getByte(i);
System.out.print((char) b);
}
System.out.println("");
//***********************************
//ctx.write(msg); // echoing
} |
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// got response from server, preparing String representation
SctpMessage sm = (SctpMessage) msg;
ByteBuf buffer = sm.content(); //
System.out.println("Response from server received: ");
for (int i = 0; i < buffer.capacity(); i ++) {
byte b = buffer.getByte(i);
System.out.print((char) b);
}
System.out.println("");
//***********************************
//ctx.write(msg); // echoing
}
Класс SctpEchoServerHandler. Добавлена строка с подтверждением:
private static final String SERVER_RESPONSE = "Acquittance from server"; |
private static final String SERVER_RESPONSE = "Acquittance from server";
В конструкторе заполняем буфер, который понадобится при каждом ответе:
public SctpEchoServerHandler() {
byte[] bytes = SERVER_RESPONSE.getBytes( Charset.forName("UTF-8" ));
response = Unpooled.buffer(SctpEchoClient.SIZE);
for (int i = 0; i < bytes.length; i++) {
response.writeByte(bytes[i]);
}
} |
public SctpEchoServerHandler() {
byte[] bytes = SERVER_RESPONSE.getBytes( Charset.forName("UTF-8" ));
response = Unpooled.buffer(SctpEchoClient.SIZE);
for (int i = 0; i < bytes.length; i++) {
response.writeByte(bytes[i]);
}
}
Опять подавляем эхо, пишем клиентское сообщение в поток, посылаем квмтанцию:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// got response from server, preparing String representation
SctpMessage sm = (SctpMessage) msg;
ByteBuf buffer = sm.content();
System.out.println("Message from client received: ");
for (int i = 0; i < buffer.capacity(); i ++) {
byte b = buffer.getByte(i);
System.out.print((char) b);
}
System.out.println("");
//***********************************
//ctx.write(msg); // echoing
ctx.write(new SctpMessage(0, 0, response));
} |
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// got response from server, preparing String representation
SctpMessage sm = (SctpMessage) msg;
ByteBuf buffer = sm.content();
System.out.println("Message from client received: ");
for (int i = 0; i < buffer.capacity(); i ++) {
byte b = buffer.getByte(i);
System.out.print((char) b);
}
System.out.println("");
//***********************************
//ctx.write(msg); // echoing
ctx.write(new SctpMessage(0, 0, response));
}
Сначала запускаем сервер:
/usr/java/jdk1.8.0_31/bin/java -cp .:sctp.jar ru.outofrange.sctp.SctpEchoServer |
/usr/java/jdk1.8.0_31/bin/java -cp .:sctp.jar ru.outofrange.sctp.SctpEchoServer
Обратите внимание: нужна java из JDK. Порт сервера 8007, хардкодный. Сервер не прекращает работу после приема одного сообщения.
Теперь запускаем клиент:
/usr/java/jdk1.8.0_31/bin/java -cp .:sctp.jar ru.outofrange.sctp.SctpEchoClient |
/usr/java/jdk1.8.0_31/bin/java -cp .:sctp.jar ru.outofrange.sctp.SctpEchoClient
Клиент посылает свой кадр. Сервер рапортует этот же кадр, затем строковое представление:
После этого посылает квитирование в кадре. Клиент принимает его, рапортует и выводит свое логгирование:
[sc:social_networks ]