Протокол SCTP является очень перспективным и предоставляет множество интересных возможностей. В данной заметке я рассмотрю использование продвинутых SCTP клиента и сервера из примеров netty (простые клиент и сервер рассмотрены тут: Простые SCTP клиент и сервер ). Проект будет на maven.
Скорее всего с ходу вам скомпилировать примеры из репозитория (репозиторий) не удастся. Я перечислю еще раз все установки и изменения, после которых пример должен заработать (возможно некоторые шаги не обязательны).
Исключение вида java.lang.UnsupportedOperationException: libsctp.so.1: cannot open shared object file: No such file or directory лечится установкой библиотеки:
yum install lksctp-tools |
Исключения java.net.SocketException: Permission denied при запуске лечатся установкой переменной окружения:
export _JAVA_OPTIONS="-Djava.net.preferIPv4Stack=true" |
и/или отключением SELinux ( https://bugs.openjdk.java.net/browse/JDK-7045222 ) — у меня установлен CentOS :
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> |
Клиент и сервер из примеров занимаются пересылкой друг другу одной и той же строки. Я изменил эту логику: клиент присылает строку, сервер делает квитирование. При этом строки извлечены из SCTP сообщения в буфер и готовы для дальнейшей обработки (у меня они просто выводятся в стандартный поток). Таким образом, изменения коснулись только хендлеров.
Я не буду приводить весь исходный код и загромождать статью. Приведу только измененные места ( исходники можно скачать тут: sctp_advanced).
Класс SctpEchoClientHandler. В конструкторе урезал размер буфера и пишу в него только читаемые символы.
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 } |
Класс SctpEchoServerHandler. Добавлена строка с подтверждением:
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]); } } |
Опять подавляем эхо, пишем клиентское сообщение в поток, посылаем квмтанцию:
@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 |
Обратите внимание: нужна java из JDK. Порт сервера 8007, хардкодный. Сервер не прекращает работу после приема одного сообщения.
Теперь запускаем клиент:
/usr/java/jdk1.8.0_31/bin/java -cp .:sctp.jar ru.outofrange.sctp.SctpEchoClient |
Клиент посылает свой кадр. Сервер рапортует этот же кадр, затем строковое представление:
После этого посылает квитирование в кадре. Клиент принимает его, рапортует и выводит свое логгирование: