如何知道Netty ByteBuf中是否没有数据可读取?

2024-02-17

我是 Netty 新手。文件传输的问题让我困惑了好几天。我想发送image文件从客户端到服务器。

下面的代码是可执行的。但只有我shutdown服务器强制我可以正常打开收到的图像文件。否则,显示“您似乎没有查看此文件的权限。检查权限并重试”。所以我想在 ByteBuf 中没有数据时关闭 fileoutputstream 使用ByteBuf.isReadable(),但是 ServerHandler 中方法 channelRead 中的 else 块永远不会到达。没用的。

此外,如果发送text文件,服务器正常时可以正常打开alive. 我不想每次传输后都关闭服务器。请给我一些建议来解决它。

这是 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));
}

}

这是文件服务器处理程序

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世界中,有多个“事件”:

  • channelActive https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#channelActive-io.netty.channel.ChannelHandlerContext-
  • channelRead https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#channelRead-io.netty.channel.ChannelHandlerContext-java.lang.Object-
  • channelReadComplete https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#channelReadComplete-io.netty.channel.ChannelHandlerContext-
  • channelInactive https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#channelInactive-io.netty.channel.ChannelHandlerContext-
  • exceptionCaught https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#exceptionCaught-io.netty.channel.ChannelHandlerContext-java.lang.Throwable-
  • more... https://netty.io/4.1/api/io/netty/channel/ChannelInboundHandler.html#method.summary

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

@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
    }
}

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

修复客户端

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

为了正确处理这些情况,Netty 使用了Future https://netty.io/4.1/api/io/netty/util/concurrent/Future.html允许代码在异步操作发生后处理事件的系统。

我们很幸运,Netty 已经有一个内置的解决方案 https://netty.io/4.0/api/io/netty/channel/ChannelFutureListener.html#CLOSE为此,我们只需将其连接起来。为了将此解决方案连接到我们的代码,我们必须跟踪最新的ChannelFuture https://netty.io/4.0/api/io/netty/channel/ChannelFuture.html由 Netty 的 write 方法发出。

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

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

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

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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何知道Netty ByteBuf中是否没有数据可读取? 的相关文章

  • 如何从Firebase Firestore实时更新文档中获取修改后的字段或数据? [复制]

    这个问题在这里已经有答案了 我有多个文档 我的问题是我无法获取修改的特定数据 我正在获取完整的文档 db collection employees whereEqualTo OID OID addSnapshotListener new E
  • “java.io.IOException:连接超时”和“SocketTimeoutException:读取超时”之间有什么区别

    如果我设置一个套接字 SoTimeout 并从中读取 当读取时间超过超时限制时 我会收到 SocketTimeoutException 读取超时 这是我的例子中的堆栈 java net SocketTimeoutException Read
  • 带有 Android 支持库 v7 的 Maven Android 插件

    我使用 maven android plugin 构建我的 android 应用程序 它依赖于 android 支持库 v4 和 v7 由于我没有找到如何从developer android com下载整个sdk 因此我无法使用maven
  • Oracle Java 教程 - 回答问题时可能出现错误

    我是 Java 新手 正在阅读 Oracle 教程 每个部分之后都有问题和答案 我不明白一个答案中的一句话 见下面的粗体线 来源是https docs oracle com javase tutorial java javaOO QandE
  • Base36 编码字符串?

    我一直在网上查找 但找不到解决此问题的方法 在 Python Ruby 或 Java 中 如何对以下字符串进行 Base 36 编码 nOrG9Eh0uyeilM8Nnu5pTywj3935kW 5 Ruby 以 36 为基数 s unpa
  • 如何使用 JAVA 代码以编程方式捕获线程转储?

    我想通过 java 代码生成线程转储 我尝试使用 ThreadMXBean 为此 但我没有以正确的格式获得线程转储 因为我们正在使用jstack命令 请任何人提供一些帮助 他们是否有其他方式获取线程转储 使用任何其他 API 我想要的线程转
  • 将SQL数据引入jquery availabletag

    我正在尝试制作自动完成文本框 但如何将 SQL 数据包含到 jquery 可用标记并循环它 我无法根据以下代码执行该功能 任何帮助 将不胜感激 谢谢 这是我的预期输出 预期结果演示 http jsfiddle net VvETA 71 jq
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • 删除优先级队列的尾部元素

    如何删除优先级队列的尾部元素 我正在尝试使用优先级队列实现波束搜索 一旦优先级队列已满 我想删除最后一个元素 优先级最低的元素 Thanks 没有简单的方法 将元素从原始元素复制到新元素 最后一个除外 PriorityQueue remov
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 寻找局部最小值

    下面的代码正确地找到了数组的局部最大值 但未能找到局部最小值 我已经进行了网络搜索 以找到找到最小值的最佳方法 并且根据这些搜索 我认为我正在使用下面的正确方法 但是 在几天的时间里多次检查每一行之后 下面的代码中有一些我仍然没有看到的错误
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • Karaf / Maven - 无法解决:缺少需求 osgi.wiring.package

    我无法在 Karaf 版本 3 0 1 中启动捆绑包 该包是使用 Maven 构建的并导入gson http mvnrepository com artifact com google code gson gson 2 3 1 我按照要求将
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 如何让 Emma 或 Cobertura 与 Maven 一起报告其他模块中源代码的覆盖率?

    我有一个带有 Java 代码的多模块 Maven 设置 我的单元测试在其中一个模块中测试多个模块中的代码 当然 这些模块具有相互依赖性 并且在测试执行之前根据需要编译所有相关模块中的代码 那么 如何获得整个代码库覆盖率的报告 注意 我不是问
  • 如何在Java中对对象数组进行字段级别排序以进行等级比较?

    In Java Class StudentProgress String Name String Grade CTOR goes here main class main method StudentProgress arrayofObje

随机推荐