Kilim的小BUG

    我最近在实现一个基于Kilim的HttpClient,在处理响应body特别大的情形下遇到了kilim的一个BUG,有必要记录下。
    问题是这样,Kilim将连接封装为EndPoint对象,EndPoint有个方法fill用于从管道读数据到缓冲区,并且可以指定希望至少读到多少个字节(atLeastN)才返回。那么在进入此方法的时候会判断缓冲区是否有足够空间容纳atLeastN个字节,如果没有,则创建一个更大的缓冲区,并将“老”的缓冲区的数据拷贝到新缓冲区,这部分代码是这样实现:

public ByteBuffer fill(ByteBuffer buf, int atleastN) throws IOException, Pausable {
        if (buf.remaining() < atleastN) {
            ByteBuffer newbb = ByteBuffer.allocate(Math.max(buf.capacity() * 3 / 2, buf.position() + atleastN));
            buf.rewind();
            newbb.put(buf);
            buf = newbb;
        }
        ……
}

    后面的代码我省略了,这个BUG就出现在这段代码里。这段代码的逻辑很简单,先是创建一个新的更大的缓冲区,然后将老的缓冲区的数据put到新的缓冲区,在put之前调用rewind方法将老的缓冲区的position设置为0。查看rewind干了什么:

 public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
    }

    仅仅是将position设置为0,并让mark失效。position指向下一个读或者写的位置,这里在写入到新缓冲区之前确实需要将position设置为0,以便写入从老的缓冲区第一个位置开始。问题是什么?问题是position仅仅指定了下一个读取数据的位置,却没有指定有效数据的大小,换句话说,没有指定老的缓冲区的limit。因此这里造成的后果是老的缓冲区整个被写入到新的老缓冲区,包括有效数据和无效数据,默认情况下缓冲区的limit等于capacity。

   这个bug可以通过下面程序看出来:

        ByteBuffer old = ByteBuffer.allocate(8);
        old.putInt(99);
        ByteBuffer newBuf = ByteBuffer.allocate(16);
        old.rewind();
        newBuf.put(old);
        newBuf.putInt(100);

        newBuf.flip();
        System.out.println(newBuf.remaining());
        System.out.println(newBuf.getInt());
        System.out.println(newBuf.getInt());
        System.out.println(newBuf.getInt());

    先往old写入一个整数99,然后创建newBuf并写入old数据,并再写入一个整数100,最后从newBuf读数据。本来我们预期只应该读到两个整数99和100,但是中间却插入一个0,输出如下:

12
99
0
100

    12表示缓冲区可读的数据,本来应该是8个字节,却多了4个字节的无效数据。

     这个BUG解决很简单,将rewind修改为flip方法即可,flip不仅将position设置为0,也将limit设置为当前位置:

  public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
    }

    修改上面的测试程序,符合我们的预期了:

        ByteBuffer old = ByteBuffer.allocate(8);
        old.putInt(99);
        ByteBuffer newBuf = ByteBuffer.allocate(16);
        old.flip();
        newBuf.put(old);
        newBuf.putInt(100);

        newBuf.flip();
        System.out.println(newBuf.remaining());
        System.out.println(newBuf.getInt());
        System.out.println(newBuf.getInt());;

    输出:

8
99
100

    总结,使用rewind的前提是limit已经正确设置,例如你将buffer写入成功并想记录这个buffer,可以使用rewind:

while (buffer.hasRemaining()) //发送数据
    networkChannel.write(buffer);
buffer.rewind(); // 重置buffer,准备写入日志管道
while (buffer.hasRemaining()) // 写入日志
    loggerChannel.write(buffer);

   而flip用于缓冲区发送或者读取之前,也就是将缓冲区设置为等待传出状态

文章转自庄周梦蝶  ,原文发布时间 2010-11-03

时间: 2024-09-28 23:39:39

Kilim的小BUG的相关文章

asp.net 2.0中tablecontrol搭配masterpage的小bug

asp.net|erp 在asp.net 2.0中,如果在一个masterpage页面中,使用服务端的table控件的话,如下所示,会在设置视图时,没了其中的contentplaceholder,     <asp:Table ID="tbl" runat="server">            <asp:TableRow>                <asp:TableCell>                    Th

c++-二叉树搜索的问题,以及一个小bug

问题描述 二叉树搜索的问题,以及一个小bug #include<iostream> #include<string> using namespace std; class node{ public: string name; string keyword; node* left; node* right; node(string a = "0", string b = "0", node* c = 0, node* d = 0) : name

Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决

原文:Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决 最近一直在开发XX的uwp版本,也是边摸索边做,最近遇到几个比较奇怪的问题,记录于此. 1.项目可用部署到PC,但无法部署到手机,提示以下错误: 错误 : DEP0001 : 意外错误: Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9 为了方便开发,我将常用的类库引用好.默认的几个页面做

menu-关于popupwindow学习中出现的一个小bug

问题描述 关于popupwindow学习中出现的一个小bug 我设置了一个buttonde点击事件 点击后能弹出popuowindow的框 ,我也设置了menu键,点击menu键 也可以弹出这个popupwindow的框 ,但是奇怪的地方在于,如果我不先点击button,我点menu键,它不会再弹出这个popupwindow的框了 ,意思就是只有先点button弹出那个popupwindow框先,点击menu才会有popupwindow这个框.我一直找不到原因,特地找各位大神帮帮忙!然后还有一个

c++-pta:大炮打蚊子,有点小bug,求大神帮忙解决一下

问题描述 pta:大炮打蚊子,有点小bug,求大神帮忙解决一下 现在,我们用大炮来打蚊子:蚊子分布在一个M×N格的二维平面上,每只蚊子占据一格.向该平面的任意位置发射炮弹,炮弹的杀伤范围如下示意: O OXO O 其中,X为炮弹落点中心,O为紧靠中心的四个有杀伤力的格子范围.若蚊子被炮弹命中(位于X格),一击毙命,若仅被杀伤(位于O格),则损失一半的生命力.也就是说,一次命中或者两次杀伤均可消灭蚊子.现在给出蚊子的分布情况以及连续k发炮弹的落点,给出每炮消灭的蚊子数. 输入格式: 第一行为两个不

Chrome出了个小bug:论如何在Chrome下劫持原生只读对象

Chrome出了个小bug:论如何在Chrome下劫持原生只读对象       概述 众所周知,虽然JavaScript是个很灵活的语言,浏览器里很多原生的方法都可以随意覆盖或者重写,比如alert.但是为了保证网页的安全性和网页制作者的一定控制权,有些浏览器对象是无法更改的,比如"window.location"对象,或者对它们的更改是无效的,比如"window.navigator"对象.然而,最近我发现Chrome出现了一个小"bug",在C

linux的一点小bug

那天,我们"操作系统"课程安排上机实验,主题是让我们熟悉 Linux 下的系统调用,内容是尝试用 fork() 创建一个子进程. 老师告诉我们:fork() 执行后,父进程和子进程共享代码段.我当时还不清楚子进程运行的时候,到底是从头开始执行还是从 fork() 开始执行.fork 这个单词的字面意思是分叉,按这个逻辑进程应该是从 fork() 这个分叉口继续执行.于是,写了如下代码检验我的推测. /** * Linux 的小 Bug * Code 1 * Author: redrai

[实录]解决Migrator.Net 小bug

好久没写了,平时比较忙,只能趁周末的时候,写一点小东西,自己也记录一下. 平时我们做项目的时候,都会有自己的数据访问层,为了能方便以后的升级,我们一般会抽象出数据访问层,利用某些方式(比如工厂模式),达到数据库类型的切换,这大大提高了我们的开发效率,只需要修改建立一个新数据库,再配置的时候修改一下就能使用了.但每次我们必须要建立这个新的数据库,有时候这个工作量也非常的大,如果不熟悉的人,还可能建立的数据库与原先的数据访问不兼容,那怎么解决呢?有什么办法使我们建立数据库能够统一呢?答案是肯定的,我

Java内部类this$0字段产生的一个小bug

首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题. 这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码.二项堆中包含一个内部类BinomialHeapEntry,这个内部类的对象即二项堆中的每一个结点,除了包含结点对应的关键字外,还记录父节点parent,下一个兄弟结点sibling和第一个孩子结点child三个指针.二项堆的根表通过每棵二项树根节点的sibling指针链接. cloneBinomialTree(BinomialHea