本文介绍了如何知道Netty ByteBuf中是否没有可读取的数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Netty的新手。文件传输存在一个问题让我困惑了好几天。我想将图像文件从客户端发送到服务器。

I'm new to Netty. There is a problem about file transfer confusing me for days. I want to send image file from client to server.

以下代码是可执行的。但只有我关闭服务器才能强行打开收到的图像文件。否则,它会显示您似乎无权查看此文件。请检查权限,然后重试。因此,当ByteBuf中没有数据使用 ByteBuf.isReadable()时,我想关闭fileoutputstream,但ServerHandler中方法channelRead中的else块永远不会到达。它没用。

The code below is executable. But only I shutdown server forcibly can I open received image file normally. Otherwise, it shows "It looks like you don't have permission to view this file. Check the permissions and try again". So I want to close fileoutputstream when there is no data in ByteBuf using ByteBuf.isReadable(), but the else block in method channelRead in ServerHandler never reach. It's useless.

此外,如果发送文本文件,当服务器活着时,它可以正常打开。 我不希望每次转移后关闭服务器。请给我一些建议来解决它。

Besides, if sending text file, it can be open normally when server is alive. I don't want to shutdown server every time after transfer. Please give me some suggestions to solve it.

这是FileClientHandler

This is FileClientHandler

public class FileClientHandler extends ChannelInboundHandlerAdapter {

private int readLength = 8;

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    sendFile(ctx.channel());
}

private void sendFile(Channel channel) throws IOException {
    File file = new File("C:\\Users\\xxx\\Desktop\\1.png");
    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);

    for (;;) {
        byte[] bytes = new byte[readLength];
        int readNum = bis.read(bytes, 0, readLength);
        // System.out.println(readNum);
        if (readNum == -1) {
            bis.close();
            fis.close();
            return;
        }
        sendToServer(bytes, channel, readNum);
    }
}

private void sendToServer(byte[] bytes, Channel channel, int length)
        throws IOException {
    channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}

}

这是FileServerHandler

This is FileServerHandler

public class FileServerHandler extends ChannelInboundHandlerAdapter {

private File file = new File("C:\\Users\\xxx\\Desktop\\2.png");
private FileOutputStream fos;

public FileServerHandler() {
    try {
        if (!file.exists()) {
            file.createNewFile();
        } else {
            file.delete();
            file.createNewFile();
        }
        fos = new FileOutputStream(file);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
        throws Exception {
    try {
        ByteBuf buf = (ByteBuf) msg;
        if (buf.isReadable()) {
            buf.readBytes(fos, buf.readableBytes());
            fos.flush();
        } else {
            System.out.println("I want to close fileoutputstream!");
            buf.release();
            fos.flush();
            fos.close();
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}
}


推荐答案

修复服务器端



在Netty世界中,有多个事件:

Fixing the server side

In the Netty world, there are multiple "events":








  • channelActive
  • channelRead
  • channelReadComplete
  • channelInactive
  • exceptionCaught
  • more...

在这些事件中,你可能已经知道了什么 channelRead 确实(因为你使用它),但你似乎需要的另一个是 channelInactive 。当另一个端点关闭连接时会调用此端口,您可以像这样使用它:

Of these "events", you probably already knows what channelRead does (since your using it), but another one that you seem to need is the channelInactive. This one is called when the other endpoint shuts the connection down, and you can use it like this:

@Override
public void channelInactive(ctx) {
    System.out.println("I want to close fileoutputstream!");
    fos.close();
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
        throws Exception {
    try {
        ByteBuf buf = (ByteBuf) msg;
        // if (buf.isReadable()) { // a buf should always be readable here
            buf.readBytes(fos, buf.readableBytes());
        //    fos.flush(); // flushing is always done when closing
        //} else {
        //    System.out.println("I want to close fileoutputstream!");
        //    buf.release(); // Should be placed in the finally block
        //    fos.flush();
        //    fos.close();
        //}
    } catch (Exception e) {
         e.printStackTrace();
    } finally {
         buf.release(); // Should always be done, even if writing to the file fails
    }
}

但是,服务器如何知道连接已关闭?目前客户端没有关闭服务器,而是继续在后台运行,永远保持连接活动。

However, how does the server know the connection has shutdown? At the moment the client does not shut the server down, and instead keep running in the background forever keeping the connection alive.

要正确关闭客户端的连接,我们需要调用 channel.close(),但是,我们不能在此之前直接插入返回行,因为这会导致发送数据和关闭网络层连接之间的竞争条件,可能会丢弃数据。

To properly shutdown the connection from the client, we need to call channel.close(), however, we cannot directly insert this before the return line, as this causes a race condition between sending the data, and closing the connection in the network layer, potentially dropping data.

为了正确处理这些条件,Netty使用系统允许代码在异步操作发生后处理事件。

To properly handle these conditions, Netty uses a Future system that allows code to handle events after asynchronous actions happen.

幸运的是,Netty已经,我们只需要连接它。要将此解决方案连接到我们的代码,我们必须跟踪最新的由Netty的写入方法发出。

Lucky for us, Netty already has a build in solution for this, we only need to wire it up. To wire this solution up to our code, we have to keep track of the latest ChannelFuture emitted by Netty's write method.

为了正确实施此解决方案,我们更改 sendToServer 返回write方法的结果:

To properly implement this solution, we change sendToServer to return the result of the write method:

private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
        throws IOException {
    return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}

然后我们继续跟踪这个返回值,并添加一个包含Netty的监听器当我们想要关闭连接时构建解决方案:

Then we keep keep track of this return value, and add a listener containing Netty's build in solution when we want to close the connection:

ChannelFuture lastFuture = null;
for (;;) {
    byte[] bytes = new byte[readLength];
    int readNum = bis.read(bytes, 0, readLength);
    // System.out.println(readNum);
    if (readNum == -1) {
        bis.close();
        fis.close();
        if(lastFuture == null) { // When our file is 0 bytes long, this is true
            channel.close();
        } else {
            lastFuture.addListener(ChannelFutureListener.CLOSE);
        }
        return;
    }
    lastFuture = sendToServer(bytes, channel, readNum);
}

这篇关于如何知道Netty ByteBuf中是否没有可读取的数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-14 15:58