С вашим сервером / клиентом не так много вещей. Прежде всего SSL, для клиента вам не нужно инициализировать SslContext для сервера, вместо этого вы должны сделать что-то вроде этого:
sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
На стороне сервера вы используете SelfSignedCertificate
, что само по себе не является ошибкой, но хотелось бы напомнить вам, что его следует использовать только для целей отладки, а не в производстве. Кроме того, вы используете ChannelOption.SO_KEEPALIVE
, что не рекомендуется, поскольку интервал поддержки активности зависит от ОС. Кроме того, вы добавили Object En-/Decoder
в свой конвейер, который в вашем случае не делает ничего полезного, поэтому вы можете удалить их.
Также вы неправильно настроили LengthFieldBasedFrameDecoder
из-за неполного и неправильного параметра list . В netty docs вам нужна версия конструктора, которая определяет lengthFieldLength
и initialBytesToStrip
. Помимо отсутствия поля длины, вы также определили неверный lengthFieldLength
, который должен совпадать с вашим LengthFieldPrepender
lengthFieldLength
, который составляет 4 байта. В заключение вы можете использовать конструктор так:
new LengthFieldBasedFrameDecoder(64 * 1024, 0, 4, 0, 4)
В обоих ваших обработчиках вы не указываете Charset
при кодировании / декодировании вашего String
, что может привести к проблемам, потому что, если не определен «Charset», будет использоваться системное значение по умолчанию, которое может варьироваться. Вы можете сделать что-то вроде этого:
//to encode the String
string.getBytes(StandardCharsets.UTF_8);
//to decode the String
new String(bytes, StandardCharsets.UTF_8);
Кроме того, вы попытались использовать DefaultFileRegion
, если в конвейер не было добавлено SslHandler
, что было бы хорошо, если бы вы не добавили LengthFieldHandler
, поскольку им потребуется копия байта [] в памяти для отправить в поле добавленной длины. Более того, я бы рекомендовал использовать ChunkedNioFile
вместо ChunkedFile
, потому что он неблокирует, что всегда хорошо. Вы бы сделали это так:
new ChunkedNioFile(randomAccessFile.getChannel())
И еще одна заключительная вещь о том, как декодировать a ChunkedFile
, так как он разбит на куски, вы можете просто объединить их вместе с помощью простого OutputStream. Вот мой старый обработчик файлов:
public class FileTransferHandler extends SimpleChannelInboundHandler<ByteBuf> {
private final Path path;
private final int size;
private final int hash;
private OutputStream outputStream;
private int writtenBytes = 0;
private byte[] buffer = new byte[0];
protected FileTransferHandler(Path path, int size, int hash) {
this.path = path;
this.size = size;
this.hash = hash;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
if(this.outputStream == null) {
Files.createDirectories(this.path.getParent());
if(Files.exists(this.path))
Files.delete(this.path);
this.outputStream = Files.newOutputStream(this.path, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
int size = byteBuf.readableBytes();
if(size > this.buffer.length)
this.buffer = new byte[size];
byteBuf.readBytes(this.buffer, 0, size);
this.outputStream.write(this.buffer, 0, size);
this.writtenBytes += size;
if(this.writtenBytes == this.size && MurMur3.hash(this.path) != this.hash) {
System.err.println("Received file has wrong hash");
return;
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if(this.outputStream != null)
this.outputStream.close();
}
}