WINVNC源码分析(三)rdr

rdr是winvnc用来io操作的一个库工程。

IO操作无非两种,写入和读取数据,所以这里每个类都相应地分成了IN和OUT两种操作,先分析IN。

 

 

 

 

 

构造函数为protected权限,摆明是给子类提供接口规范,derived class。

两个变量根据名字来分析,ptr指向的应该是当前位置,end则是最后一个字节。

inline int check(int itemSize, int nItems=1)

检查剩余缓存是否够用,代码对三种情况进行了处理,当前缓存不足一个itemSize的时候会运行overrun。overrun在这里是一个纯虚函数,稍后我们可以在子类中它的应用。

 

readU系列

以readU32为例,这里没有用memcpy进行拷贝,而是将源低地址的8位赋值给目标变量的高8位,既默认大端字节序(网络字节序)。

readS只是将readU的返回值作一个有符号类型的强制转换。

char* InStream::readString()

这里默认当前缓存头32位保存的是将要读取内容的长度,然后动态分配了相应长度(+1保证C STYLE字符串),拷贝后返回头指针,意味着由调用函数者来释放。上限是U32 InStream::maxStringLength = 65535;

 inline void skip(int bytes)

用来快进当前指针bytes个字节。

virtual void readBytes(void* data, int length)

读取指定长度length的内容到data。这里用了while循环,虽然之前看到了有overrun函数,所以貌似有不读到就阻塞的嫌疑。

 

readOpaque系列则

这里就是一个字节一个字节的原始拷贝了。因为一个类型指针在内存中永远指向的是低地址。附一个判断CPU大小端的函数

 

 

 

 

class FdInStream : public InStream

这个类是IO端口的一个缓存,从IO端口读取数据,并且计算流量。

 

 

这个是从IO读取长度len数据到buf。

1 用select函数检查端口是否有数据到达。

2 如果没有的话调用构造函数传入的回调函数blockCallback。

3 然后读取数据,虽然::read(fd, buf, len);用了全局符号,但是头文件有定义#define read(s,b,l) recv(s,(char*)b,l,0)。

4 计算流量,先看下获取时间的函数:尝试用两种办法获取当前时间

static void gettimeofday(struct timeval* tv, void*)

 

 

startTiming、stopTiming和kbitsPerSecond分别用于开始计算、停止计算和返回从开始至今的传输率。

但都作了最小和最大的传输率限制,暂时猜不透用意。

 

现在我们看看基类中没有实现的虚函数

FdInStream::overrun(int itemSize, int nItems)

 

对于比缓冲还大的itemsize只能吼下“OUT”

overrun是在缓冲区剩余未读长度小于itemsize的时候运行的,我们先得理解下几个变量:

ptr 缓冲区中未读数据的首字节地址

end 缓冲区中未读数据的尾字节地址

start 永远都指向了缓冲区的首字节地址

offset 数据传输总量。

首先memmove(start, ptr, end - ptr);把缓冲区的未读数据拷贝到缓冲区的头部,然后调用FdInStream::readWithTimeoutOrCallback从IO读取数据填充缓冲区剩余部分。

对于大量的数据,我们首先把缓冲区中未读数据读取出来,然后调用FdInStream::readWithTimeoutOrCallback直接读取到目标地址,而不再经过缓存区,这样可以提高效率。

也就是说缓冲区虽然方便了管理数据读写管理,但对效率相应地有些影响。

 

 

 

 

class MemInStream : public InStream

int overrun(int itemSize, int nItems) { throw EndOfStream(); }

可以看出来这个类用于对内存数据的管理。

 

 

 

ZlibInStream : public InStream

本来想到研究zlib库的时候再说,但zlib的接口类非常简洁。它增加的几个变量

 

构造函数:

 

B的初始化。

void ZlibInStream::setUnderlying(InStream* is, int bytesIn_)

 

A的初始化。

int ZlibInStream::overrun(int itemSize, int nItems)

 

对照FdInStream::overrun函数,看出它是调用了核心处理函数decompress来解决无数据可读的情况

void ZlibInStream::decompress()

 

它是逻辑很简单,压缩前初始化B的几个关键变量,然后直接调用压缩函数inflate,压缩完后,我们要相应地修改A和C的成员指针到相应地偏移量。

 

void ZlibInStream::reset()

 

 

把A中需要压缩的数据全部压缩完并输出到C中,然后初始化成员变量。我们可以通过函数setUnderlying来切换A,但是记住在之前调用RESET。

 

这个时候看OUT系列就轻松很多了。

class FdOutStream : public OutStream

void FdOutStream::flush()

 

所谓的flush就是把缓冲区的数据写入IO。

FdOutStream::writeBytes(const void* data, int length)

 

发现对于大量数据写入也是先刷新缓冲区,然后绕过缓冲区直接IO写入。

 

在这里可以清晰地看出offset的用途

它就是用来记录写入IO的数据总量。

 

 

int FdOutStream::length()

{

  return offset + ptr - start;

}

当然对于外界得到的只能是通过OutStream接口写入的数据总量,因为它会把缓冲区的当前数据量加上去。

 

 

 

 

MemOutStream

唯一要注意的就是int overrun(int itemSize, int nItems)

在这里如果原来的缓冲不够写,那我们就重新分配一个足够大的缓冲区,把原来缓冲区已写入的数据拷贝过来,然后释放掉旧的缓冲区。

 

 

 

class NullOutStream : public OutStream

这个家伙根本就没有作任何数据的操作,仅仅是计量,猜测是用来计算数据吞吐量的。

 

 

class ZlibOutStream : public OutStream

成员变量:

 

 

构造函数同样是初始化了B。但这里调用的deflateInit。

void ZlibOutStream::flush()

 

 

保证了A中所有需要压缩的数据全部压缩并输出。

int ZlibOutStream::overrun(int itemSize, int nItems)

 

这个函数并没直接调用flush来解决A空间不足的问题,因为只要保证A中有空间满足itemSize就好,而不是flush这种函数完全清空。

void ZlibOutStream::setUnderlying(OutStream* os)

 

重置A,记得在它之前调用ZlibOutStream::flush。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值