thrift之TTransport层的分帧传输类TFramedTransport
帧传输类就是按照一帧的固定大小来传输数据,所有的写操作首先都是在内存中完成的直到调用了flush操作,然后传输节点在flush操作之后将所有数据根据数据的有效载荷写入数据的长度的二进制块发送出去,允许在接收的另一端按照固定的长度来读取。
帧传输类同样还是从缓存基类TBufferBase继承而来,实现的接口当然也基本相同,只是实现的方式不同而已,下面就来看看具体的实现过程和原理。
这个类所采用的默认缓存长度是512(static const int DEFAULT_BUFFER_SIZE = 512;),两个基本构造函数一个采用默认的缓存长度,另一个可以指定一个需要的缓存长度。下面还是重点分析慢读、读帧等操作的实现过程:
(1)慢读实现如下:
uint32_t TFramedTransport::readSlow(uint8_t* buf, uint32_t len) {
uint32_t want = len;//想要读取的长度
uint32_t have = rBound_ - rBase_;//内存缓存中已经有的数据的长度
assert(have < want);//如果以后数据长度满足需要读的长度就不需要采用慢读
// 如果我们有一些数据在缓存,拷贝出来并且返回它。
// 我们没有试图读取更多的数据而是不得不返回它,因为我们不能保证在下面的
// 传输层实际上有更多的数据,因此应该尝试阻塞式读它。
if (have > ) {
memcpy(buf, rBase_, have);//拷贝出缓存中已有的数据
setReadBuffer(rBuf_.get(), );//重新设置缓存基地址
return have;//返回
}
// 读取另一帧。
if (!readFrame()) {
// EOF. No frame available.
return ;
}
// 处理我们已有的数据
uint32_t give = std::min(want, static_cast<uint32_t>(rBound_ - rBase_));//已有数据想要读取长度取短的
memcpy(buf, rBase_, give);//拷贝
rBase_ += give;//调整缓存基地址
want -= give;//计算还有多少想要的数据没有得到
return (len - want);//返回实际读取长度
}
缓存中没有数据的时候就会调用读取帧的函数readFrame,这个函数实现如下:
bool TFramedTransport::readFrame() {
//首先读下一帧数据的长度
int32_t sz;//存放长度的变量
uint32_t size_bytes_read = ;//读取长度数据的字节数
while (size_bytes_read < sizeof(sz)) {//表示长度的数据小于存放长度数据的字节数
uint8_t* szp = reinterpret_cast<uint8_t*>(&sz) + size_bytes_read;//长度变量转换为指针
uint32_t bytes_read = transport_->read(szp, sizeof(sz) - size_bytes_read);//读取
if (bytes_read == ) {//如果返回为0表示没有数据了
if (size_bytes_read == ) {//没有任何数据读到,返回false
return false;
} else {
// 部分的帧头部,抛出异常。
throw TTransportException(TTransportException::END_OF_FILE,
"No more data to read after "
"partial frame header.");
}
}
size_bytes_read += bytes_read;//以读取的长度
}
sz = ntohl(sz);//长整数的网络字节序转换为主机字节序
if (sz < ) {//帧的长度不能是负数涩,抛出异常
throw TTransportException("Frame size has negative value");
}
// 读取有效数据负载,重新设置缓存标记。
if (sz > static_cast<int32_t>(rBufSize_)) {
rBuf_.reset(new uint8_t[sz]);//接收基地址
rBufSize_ = sz;//缓存大小
}
transport_->readAll(rBuf_.get(), sz);//调用readAll读取sz长度的数据
setReadBuffer(rBuf_.get(), sz);//设置读缓存基地址
return true;
}
从上面实现代码看出,在按帧读取的过程中,首先需要读取这一帧的头部信息,而这个头部信息就是这一帧的长度,后面就根据头部信息中给定的长度来读取数据部分,读出来的数据放入缓存中。读取头部信息时注意处理异常的情况,还有就是读出来的数据需要经过网络字节序到主机字节序的转换。下面继续看慢写函数和flush刷新函数的实现过程,慢写函数实现如下(快读和快写基类TBufferBase的实现已经满足要求了,所以不需要再去单独实现了):
void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) {
// 直到有足够的双缓冲大小
uint32_t have = wBase_ - wBuf_.get();//缓存空间已经有多少数据
uint32_t new_size = wBufSize_;
if (len + have < have /* overflow */ || len + have > 0x7fffffff) {//如果长度溢出或大于2GB了
throw TTransportException(TTransportException::BAD_ARGS,
"Attempted to write over 2 GB to TFramedTransport.");//抛出异常
}
while (new_size < len + have) {//缓存空间的长度小于已有数据的长度和需要写入数据长度的和
new_size = new_size > ? new_size * : ;如果缓存空间长度是大于0的话就扩容一倍的空间
}
uint8_t* new_buf = new uint8_t[new_size];// 分配新空间
memcpy(new_buf, wBuf_.get(), have);// 拷贝已有的数据到新空间.
wBuf_.reset(new_buf);// 缓存地址重新设置
wBufSize_ = new_size;// 缓存新长度
wBase_ = wBuf_.get() + have;//新的开始写入地址
wBound_ = wBuf_.get() + wBufSize_;//写入界限
memcpy(wBase_, buf, len);//拷贝数据到新缓存地址
wBase_ += len;//更新缓存基地址
}
上面代码就是实现把从上层传输的数据写入缓存中以供下层发送使用,这段代码需要注意的是while循环,这个while循环保证有足够的缓存来存放写入的数据到缓存中,每次增长的长度是上次的一倍;还需要注意的是,分配了新的空间需要把原来还没有真正写入的数据拷贝到新缓存中来,不然就会造成内容丢失;最后就是更新缓存的基地址和长度等描述缓存的信息。继续看flush函数的实现代码:
void TFramedTransport::flush() {
int32_t sz_hbo, sz_nbo;
assert(wBufSize_ > sizeof(sz_nbo));//断言缓存长度应该大于个字节sizeof(int32_t)
sz_hbo = wBase_ - (wBuf_.get() + sizeof(sz_nbo));// 滑动到第一帧数据的开始位置。
sz_nbo = (int32_t)htonl((uint32_t)(sz_hbo));//主机字节序转换为网络字节序
memcpy(wBuf_.get(), (uint8_t*)&sz_nbo, sizeof(sz_nbo));//头部长度拷贝写缓存
if (sz_hbo > ) {//保证缓存有需要写入的数据
//如果底层传输写抛出了异常注意确保我们处于安全的状态
//(例如内部缓冲区清理),重置我们写入前的状态(因为底层没有传输成功)
wBase_ = wBuf_.get() + sizeof(sz_nbo);//得到
// 写入长度和帧
transport_->write(wBuf_.get(), sizeof(sz_nbo)+sz_hbo);
}
// 刷新底层传输.
transport_->flush();
}
刷新函数就是把缓存中的数据真正的发送出去,但是在写入到底层时,底层传输可能不会真正成功(如网络突然断了),这个时候底层会抛出异常,那么我们需要捕获异常,以便重新处理这些数据,只有数据真正写入成功的时候我们才计算我们写如数据的长度。所以还有写结束和读结束函数writeEnd、readEnd,它们都只有简单的一句代码就是计算真正完成读写数据的长度。
整个按帧传输的类的功能介绍完毕了,主要需要注意的就是缓存的操作,保证数据不丢失。将来实现考虑分配内存使用c语言的malloc类函数,而不是使用new操作,这样也能提高不少的效率。
thrift之TTransport层的分帧传输类TFramedTransport的更多相关文章
- thrift之TTransport层的内存缓存传输类TMemoryBuffer
内存缓存是简单的在内存进行读写操作的一种传输,任何时候想在上面写入数据都是放入缓存中,任何时候读操作数据也是来至于缓存.内存缓存的分配使用c语言的malloc类函数,分配的长度是需要长度的两倍,需要考 ...
- thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
本节主要介绍缓冲相关的传输类,缓存的作用就是为了提高读写的效率.Thrift在实现缓存传输的时候首先建立一个缓存的基类,然后需要实现缓存功能的类都可以直接从这个基类继承.下面就详细分析这个基类以及一个 ...
- thrift之TTransport层的堵塞的套接字I/O传输类TSocket
本节将介绍第一个实现具体传输功能的类TSocket,这个类是基于TCP socket实现TTransport的接口.下面具体介绍这个类的相关函数功能实现. 1.构造函数 分析一个类的功能首先看它的定义 ...
- thrift之TTransport类体系原理及源码详细解析1-类结构和抽象基类
本章主要介绍Thrift的传输层功能的实现,传输的方式多种多样,可以采用压缩.分帧等,而这些功能的实现都是相互独立,和上一章介绍的协议类实现方式比较雷同,还是先看看这部分的类关系图,如下: 由上面的类 ...
- HTTP2.0的二进制分帧
1.帧的类型: 在二进制分帧的结构中,头部有8个字节(64Bit),其中有一个字节(8Bit)来标志帧的类型: HTTP2.0规定了如下帧类型: DATA: 用于传输HTTP消息体 HEADERS:用 ...
- thrift之默认传输类TTransportDefaults和虚拟传输类TVirtualTransport
默认传输类TTransportDefaults提供了抽象类TTransport的默认实现,实现了非虚拟的方法(*_virt) read(), readAll(), write(),borrow() a ...
- 在使用TCP协议进行消息发送时,对消息分帧
成帧与解析 阅读 <java TCP/IP Socket 编程>第三章笔记 成帧技术(frame)是解决如何在接收端定位消息的首尾位置的问题.在进行数据收发时,必须指定消息接收者如何确定何 ...
- UDS帧传输
说明 在UDS协议中,其中有一点我视作为基础,即帧传输.也即是数据传输这一块,在UDS的帧传输中,分为4种: SF单帧 FF第一帧 CF连续帧 FC流控制帧 首先,我们抛开以上的东西,假设一个销售商( ...
- HTML的窗口分帧
下面通过一个后台管理的部分设计来说明窗口分帧 frameset.html代码 <!-- <frameset>标签(常用来做后台管理界面) 属性:rows(行).cols(列).可以使 ...
随机推荐
- Ubuntu修改hosts方法
1.修改hostssudo gedit /etc/hosts如果不喜欢使用gedit命令,而且当前帐户为非root帐户,那么可把/etc/hosts复制到桌面上,然后手动编辑后保存,再使用命令copy ...
- 分布式Hbase-0.98.4在Hadoop-2.2.0集群上的部署
fesh个人实践,欢迎经验交流!本文Blog地址:http://www.cnblogs.com/fesh/p/3898991.html Hbase 是Apache Hadoop的数据库,能够对大数据提 ...
- MVC学习(四)几种分页的实现(3)
在这篇MVC学习(四)几种分页的实现(2)博文中,根据URL中传入的两个参数(页码数,首页.上一页.下一页.末页的标记符)来获得对应的分页数据, 只是传入的参数太多,调用起来不太方便(标记符不能够写错 ...
- HDU 1540 Tunnel Warfare(线段树+区间合并)
http://acm.hdu.edu.cn/showproblem.php?pid=1540 题目大意:抗日战争期间进行地道战,存在n个村庄用地道连接,输入D表示破坏某个村庄(摧毁与其相连的地道, 包 ...
- mormot THttpApiServer使用例子
mormot THttpApiServer使用例子 THttpApiServer封装了WINDOWS的HTTPS.SYS. unit Unit1; interface uses Winapi.Wind ...
- Linux如何学习
一:如何提问 1. 尝试自己解决 帮助文档 示例 2. 提问的要求 问题要详细(能被别人看懂, 一个知识点) 报错信息(截图) 二:1.Linux区分大小写 2.所有内容以文件形式保存,包括硬件(一切 ...
- python 中 深拷贝和浅拷贝的理解
在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解. 在python中,对象的赋值的其实就是对象的引用.也就是说,当创建一个对象,然后赋给另外一 ...
- android system.img
哥们要我做些模拟包,给过来的是mtk的底包,需要从system.img中提取部分文件. 网上一找资料,说是yaffs2文件系统,同时以前做linux的时候也是用yaffs2,感觉碰到老朋友了,不管三七 ...
- WebStorm License Activation (WebStorm许可证激活)
User or company name(用户或公司名称): EMBRACE License key(许可证密钥): ===== LICENSE BEGIN =====89374-12042010 ...
- JS-怎么得到局部域中的数据
1,使用全局变量 var str = '';function fn1(){ var a = '大鸡腿~'; str = a;} 2,使用一个局部函数 function fn2(){ ...