BufferedStream的作用是给另一流上的读写操作添加一个缓冲层,改进IO效率。但最近在使用Npgsql(内部使用BufferedStream包装了NetworkStream)的过程中,发现BufferedStream有2个严重的问题,或者可以说是Bug。
1)BufferedStream不能同时读写
BufferedStream同时提供了读和写的API(这可能也是Stream的悲哀,不像Java中严格区分读Stream和写Stream),但是却不能同时读写。通过反编译BufferedStream,发现其内部结构有点像是工作在半双工模式下的通信接口,可以交替读写,但这个交替读写是有Bug的。
BufferedStream内部只有一个缓冲区,由读写操作共用。
a)读的时候,如果发现缓冲区中有未写完的数据,则写完缓冲区中的数据到底层流并Flush。
b)写的时候,如果发现缓冲区中有未读完的数据,则通过Seek()回退掉未读完的数据。
这套逻辑看上去很美,但是并不是每个流都支持Seek(),比如NetworkStream。底层流是
NetworkStream的情况下就可能会莫名其妙的地方报出 NotSupportedException异常。
那在使用BufferedStream时,确保读完再写可不可以?
有些应用场景这一招管用,但至少对Npgsql不行。Npgsql严格按照发请求,读响应的逻辑和服务端通信。但却忘了
PostgreSQL中存在所谓的异步消息,PostgreSQL服务端可能会在通常的响应消息后面插入一个异步通知。如果响应消息和异步通知都被读入BufferedStream的内部缓冲区,但Npgsql在这一轮请求循环中,只读出了和请求对应的响应消息,那么下次发送请求时就会触发上面提到的NotSupportedException异常。
解决方法:
比较简单的解决方法是,使用2个BufferedStream对象包装同一个底层流,分别用于读和写。
2)Read()的不当阻塞
(v=vs.80).aspx
-
int Read([In, Out] byte[] array, int offset, int count)
-
读入 array 中的总字节数。如果可用的字节没有所请求的那么多,总字节数可能小于请求的字节数;或者如果在可读取任何数据前就已到达流的末尾,则为零。
当count小于BufferedStream对内部缓冲区的大小(默认4K),并且底层流中的有效数据小于count时,会导致
Read()方法无限阻塞。
这应该是个Bug,根据MSDN中Read()的API描述(和流的Read()方法的惯例),这时应该将底层流已有的数据返回。
阅读(5017) | 评论(2) | 转发(0) |