历史溯源

由于历史原因,我们目前看到的大部分的网络协议都是基于ASCII码这种纯文本方式,也就是基于字符串的命令行方式,比如HTTP、FTP、POP3、SMTP、Telnet等。早期操作系统UNIX(或DOS),用户操作界面就是控制台,控制台的输入输出方式就决定了用户只能通过敲击键盘的方式将协议命令输入到网络,这也就导致了回车换行"\r\n"会作为一次命令结束的标识。

比如HTTP协议,与主机建立连接后,输入"GET / HTTP/1.1\r\n"即可获取网站的主页。

比如email协议,早期的电子邮件协议只支持ASCII码这种纯文本传输,但随着全世界人民对物质文化生活的不断向往,这种落后的传输方式,已经无法满足世界人民对美好生活的追求,比如图像、视频、音频、Office文件如何在邮件中展现?不同国家(非英语国家)字符集该如何传输和展现?

换句话说,就是这种非ASCII的二进制富文本,该如何传输和呈现?

MIME的诞生

此时MIME标准诞生了,MIME的出现更多的是一种向下兼容的无奈,而不是革命。通过对二进制数据或非ASCII码数据进行base64或quoted-printable编码,来实现纯ASCII码的传输。显然这种方式会让你的邮件体变大,传输效率下降。尤其附件很多时,通过MIME的boundary来解析邮件的附件也是一笔额外的负担。

同时MIME的标准也被HTTP协议所采用,我们可以通过content-type指定传输的内容是什么类型,通过MIME的boundary来对Form-Data数据进行扩展,让我们Post数据时也能够在“表格”数据中插入文件,从而达到上传文件的效果

显然这种方式不如二进制简洁,但却非常的直观,所见即所得,一眼就能看明白。但就传输效率上不如二进制方式。

又比如websocket协议虽然建立会话时采用的是HTTP协议,但后续的数据帧格式却是一个二进制格式。如下:

在这种格式下,为了表示每帧数据长度,就一定会有一个“数据长度”项,比如上面的payload len,当该值小于126时,直接表示数据区(payload data)长度;为126时用后面的2个字节表示数据区长度,为127时用后面的8个字节表示数据区长度。此时就涉及到了网络字节序和主机字节序的转换,如果数据区是一个二进制内容的话,我们就很难使用string的操作方式将整个数据报文拼接起来(可以用memcpy来拼接)。当然,我们这篇文章不是对websocket协议的讲解,而是通过该协议的数据区引出二进制数据流封装的必要性。如果是文本协议,各种开发语言对string的封装已经足够强大,已经没有封装的必要。除非你想重新改造字符串操作来提升效率或其它目的,比如我的前一篇文章:
为何写服务器程序需要自己管理内存,从改造std::string字符串操作说起。。。
话不多说,下面是一个简单的数据流的封装类CDataStream,非常简单。

.h头文件


#include <windows.h>// 数据流
class CDataStream
{public:CDataStream(BOOL bNetworkOrder = FALSE);virtual ~CDataStream();// 关联一块streamvoid Attach(const BYTE* pStream, int iStreamSize){m_pStream = (BYTE*)pStream;m_iStreamSize = iStreamSize;m_iCurrPos = 0;}// 解除关联void Detach(){m_pStream = NULL;m_iStreamSize = 0;m_iCurrPos = 0;}void Reset(){m_iCurrPos = 0;}// 获取流数据const BYTE* GetStreamData(){return m_pStream;}int GetStreamSize(){return m_iCurrPos;}// 在当前位置上移动iDistance距离int Offset(int iDistance);// 移动到新位置int MoveTo(int iNewPos);void MoveToBegin(){m_iCurrPos = 0;}void MoveToEnd(){m_iCurrPos = m_iStreamSize;}// 读写字节void WriteByte(BYTE byValue);BYTE ReadByte();// 读写WORDvoid WriteWord(WORD wValue);WORD ReadWord();// 读写DWORDvoid WriteDWord(DWORD dwValue);DWORD ReadDWord();// 读写int64void WriteInt64(__int64 i64Value);__int64 ReadInt64();// 读写Floatvoid WriteFloat(float fValue);float ReadFloat();// 读写doublevoid WriteDouble(double dValue);double ReadDouble();// 读写数据流void WriteData(unsigned char* pData, int iDataLen);BYTE* ReadData(int iDataLen);// 读写字符串void WriteString(const char* pszValue);const char* ReadString();// =============运算符重载=============CDataStream& operator<<(BYTE byValue)       {   WriteByte(byValue); return *this;   }CDataStream& operator<<(WORD wValue)     {   WriteWord(wValue);  return *this;   }CDataStream& operator<<(DWORD dwValue)       {   WriteDWord(dwValue); return *this;  }CDataStream& operator<<(__int64 i64Value)    {   WriteInt64(i64Value); return *this; }CDataStream& operator<<(float fValue)        {   WriteFloat(fValue); return *this;   }CDataStream& operator<<(double dValue)       {   WriteDouble(dValue);    return *this;   }CDataStream& operator<<(const char* pszValue)    {   WriteString(pszValue); return *this;    }CDataStream& operator>>(BYTE& byValue)       {   byValue = ReadByte();  return *this;   }CDataStream& operator>>(WORD& wValue)        {   wValue = ReadWord();   return *this;   }CDataStream& operator>>(DWORD& dwValue)      {   dwValue = ReadDWord(); return *this;   }CDataStream& operator>>(__int64& i64Value)   {   i64Value = ReadInt64();    return *this;   }CDataStream& operator>>(float& fValue)       {   fValue = ReadFloat();  return *this;   }   CDataStream& operator>>(double& dValue)       {   dValue = ReadDouble(); return *this;   }CDataStream& operator>>(const char*& pszValue)   {   pszValue = ReadString();   return *this;   }public:// WORD值反序static WORD Swap(WORD wValue){WORD wRet = 0;((BYTE*)&wRet)[0] = ((BYTE*)&wValue)[1];((BYTE*)&wRet)[1] = ((BYTE*)&wValue)[0];return wRet;}// DWORD反序static DWORD Swap(DWORD dwValue){DWORD dwRet = 0;((BYTE*)&dwRet)[0] = ((BYTE*)&dwValue)[3];((BYTE*)&dwRet)[1] = ((BYTE*)&dwValue)[2];((BYTE*)&dwRet)[2] = ((BYTE*)&dwValue)[1];((BYTE*)&dwRet)[3] = ((BYTE*)&dwValue)[0];return dwRet;}// i64(long long)反序static __int64 Swap(__int64 i64Value){__int64 i64Ret = 0;((BYTE*)&i64Ret)[0] = ((BYTE*)&i64Value)[7];((BYTE*)&i64Ret)[1] = ((BYTE*)&i64Value)[6];((BYTE*)&i64Ret)[2] = ((BYTE*)&i64Value)[5];((BYTE*)&i64Ret)[3] = ((BYTE*)&i64Value)[4];((BYTE*)&i64Ret)[4] = ((BYTE*)&i64Value)[3];((BYTE*)&i64Ret)[5] = ((BYTE*)&i64Value)[2];((BYTE*)&i64Ret)[6] = ((BYTE*)&i64Value)[1];((BYTE*)&i64Ret)[7] = ((BYTE*)&i64Value)[0];return i64Ret;}// 下面的函数也是将64位长整形反序,但比较难理解,不如上面的函数简单、粗暴和直观// 即使你现在能整明白,下次未必能“见字如面”static __int64 Swap64(__int64 i64Value){return i64Value >> 56|(i64Value & 0x00ff000000000000) >> 40 |(i64Value & 0x0000ff0000000000) >> 24 |(i64Value & 0x000000ff00000000) >> 8  | (i64Value & 0x00000000ff000000) << 8  | (i64Value & 0x0000000000ff0000) << 24 |(i64Value & 0x000000000000ff00) << 40 |i64Value << 56;}// 浮点型按照IEEE745标准不存在网络字节序和机器字节序,这里只是给出实现方法// float反序static float Swap(float fValue){float fRet = fValue;Swap((BYTE*)&fRet, sizeof(float));return fRet;}// double反序static double Swap(double dValue){double dRet = dValue;Swap((BYTE*)&dRet, sizeof(double));return dRet;}// 内存数据反序static void Swap(BYTE* pData, int iDataLen);// 内存反序后返回新内存static BYTE* SwapClone(BYTE* pData, int iDataLen);protected:BOOL m_bNetworkOrder;        // 数据流是否为网络字节序,缺省为FALSEBYTE *m_pStream;          // stream缓存int m_iStreamSize;           // 缓存大小 int m_iCurrPos;             // 当前数据位置
};

.cpp实现文件

#include "DataStream.h"
#include <assert.h>
#include <stdlib.h>// 将一块内存反序
void CDataStream::Swap(BYTE* pData, int iDataLen)
{if(NULL == pData || iDataLen <= 0)return;for(int i = 0 ; i < iDataLen / 2; i++)  {  BYTE temp = pData[i];  pData[i] = pData[iDataLen - i - 1];  pData[iDataLen - i - 1] = temp;  }
}// 将一块内存反序后返回新内存
BYTE* CDataStream::SwapClone(BYTE* pData, int iDataLen)
{if(NULL == pData || iDataLen <= 0)return NULL;BYTE* pSwap = (BYTE*)malloc(iDataLen);int j = 0;for(int i = iDataLen-1; i >= 0; i--){pSwap[j]  = pData[i];j++;}return pSwap;
}//
// Construction/Destruction
//
CDataStream::CDataStream(BOOL bNetworkOrder)
{m_bNetworkOrder = bNetworkOrder;      m_pStream = NULL;m_iStreamSize = 0;m_iCurrPos = 0;
}CDataStream::~CDataStream()
{m_pStream = NULL;m_iStreamSize = 0;m_iCurrPos = 0;
}// 在当前位置上移动iDistance距离
int CDataStream::Offset(int iDistance)
{int iNewPos = m_iCurrPos+iDistance;if(iNewPos < 0)m_iCurrPos = 0;else if(iNewPos > m_iStreamSize)m_iCurrPos = m_iStreamSize;elsem_iCurrPos = iNewPos;return m_iCurrPos;
}// 移动到新位置
int CDataStream::MoveTo(int iNewPos)
{if(iNewPos < 0)m_iCurrPos = 0;else if(iNewPos > m_iStreamSize)m_iCurrPos = m_iStreamSize;elsem_iCurrPos = iNewPos;return m_iCurrPos;
}// 读写字节
void CDataStream::WriteByte(BYTE byValue)
{assert(m_iCurrPos+1 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+1 > m_iStreamSize)return;*(m_pStream+m_iCurrPos) = byValue;m_iCurrPos++;
}
BYTE CDataStream::ReadByte()
{assert(m_iCurrPos+1 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+1 > m_iStreamSize)return 0;BYTE byValue = *(m_pStream+m_iCurrPos);m_iCurrPos++;return byValue;
}// 读写WORD
void CDataStream::WriteWord(WORD wValue)
{assert(m_iCurrPos+2 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+2 > m_iStreamSize)return;// 如果是网络字节流则反序if(m_bNetworkOrder)wValue = Swap(wValue);*(WORD*)(m_pStream+m_iCurrPos) = wValue;m_iCurrPos += 2;
}
WORD CDataStream::ReadWord()
{assert(m_iCurrPos+2 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+2 > m_iStreamSize)return 0;WORD wValue = *(WORD*)(m_pStream+m_iCurrPos);m_iCurrPos += 2;// 如果是网络字节流则反序if(m_bNetworkOrder)wValue = Swap(wValue);return wValue;
}// 读写DWORD
void CDataStream::WriteDWord(DWORD dwValue)
{assert(m_iCurrPos+4 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+4 > m_iStreamSize)return;// 如果是网络字节流则反序if(m_bNetworkOrder)dwValue = Swap(dwValue);*(DWORD*)(m_pStream+m_iCurrPos) = dwValue;m_iCurrPos += 4;
}
DWORD CDataStream::ReadDWord()
{assert(m_iCurrPos+4 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+4 > m_iStreamSize)return 0;DWORD dwValue = *(DWORD*)(m_pStream+m_iCurrPos);m_iCurrPos += 4;// 如果是网络字节流则反序if(m_bNetworkOrder)dwValue = Swap(dwValue);return dwValue;
}// 读写int64
void CDataStream::WriteInt64(__int64 i64Value)
{assert(m_iCurrPos+8 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+8 > m_iStreamSize)return;// 如果是网络字节流则反序if(m_bNetworkOrder)i64Value = Swap(i64Value);*(__int64*)(m_pStream+m_iCurrPos) = i64Value;m_iCurrPos += 8;
}
__int64 CDataStream::ReadInt64()
{assert(m_iCurrPos+8 <= m_iStreamSize);            // 越界断言if(m_iCurrPos+8 > m_iStreamSize)return 0;__int64 i64Value = *(__int64*)(m_pStream+m_iCurrPos);m_iCurrPos += 8;// 如果是网络字节流则反序if(m_bNetworkOrder)i64Value = Swap(i64Value);return i64Value;
}// 读写float
void CDataStream::WriteFloat(float fValue)
{int iFloatSize = sizeof(float);assert(m_iCurrPos+iFloatSize <= m_iStreamSize);if(m_iCurrPos+iFloatSize > m_iStreamSize)return;*(float*)(m_pStream+m_iCurrPos) = fValue;m_iCurrPos += iFloatSize;
}
float CDataStream::ReadFloat()
{int iFloatSize = sizeof(float);assert(m_iCurrPos+iFloatSize <= m_iStreamSize);if(m_iCurrPos+iFloatSize > m_iStreamSize)return 0;float fValue = *(float*)(m_pStream+m_iCurrPos);m_iCurrPos += iFloatSize;return fValue;
}// 读写double
void CDataStream::WriteDouble(double dValue)
{int iDoubleSize = sizeof(double);assert(m_iCurrPos+iDoubleSize <= m_iStreamSize);if(m_iCurrPos+iDoubleSize > m_iStreamSize)return;*(double*)(m_pStream+m_iCurrPos) = dValue;m_iCurrPos += iDoubleSize;
}
double CDataStream::ReadDouble()
{int iDoubleSize = sizeof(double);assert(m_iCurrPos+iDoubleSize <= m_iStreamSize);if(m_iCurrPos+iDoubleSize > m_iStreamSize)return 0;double dValue = *(double*)(m_pStream+m_iCurrPos);m_iCurrPos += iDoubleSize;return dValue;
}// 读写数据流
void CDataStream::WriteData(unsigned char* pData, int iDataLen)
{if(NULL == pData || iDataLen <= 0)return;assert(m_iCurrPos + iDataLen <= m_iStreamSize);    // 越界断言 if(m_iCurrPos + iDataLen > m_iStreamSize)return;memcpy(m_pStream+m_iCurrPos, pData, iDataLen);m_iCurrPos += iDataLen;
}
BYTE* CDataStream::ReadData(int iDataLen)
{if(iDataLen <= 0 || m_iCurrPos >= m_iStreamSize)return NULL;assert(m_iCurrPos + iDataLen <= m_iStreamSize);   // 越界断言 if(m_iCurrPos + iDataLen > m_iStreamSize)return NULL;BYTE* pData = m_pStream+m_iCurrPos;m_iCurrPos += iDataLen;return pData;
}// 读写字符串
void CDataStream::WriteString(const char* pszValue)
{if(NULL == pszValue)return ;int iStrLen = strlen(pszValue)+1;                      // 末尾0assert(m_iCurrPos+iStrLen <= m_iStreamSize);         // 越界断言if(m_iCurrPos+iStrLen > m_iStreamSize)return;memcpy(m_pStream+m_iCurrPos, pszValue, iStrLen);m_iCurrPos += iStrLen;
}
const char* CDataStream::ReadString()
{if(m_iCurrPos >= m_iStreamSize)return NULL;int iCurrPos = m_iCurrPos;char* psz = (char*)(m_pStream+m_iCurrPos);             // 字符串位置while(iCurrPos < m_iStreamSize){if(!m_pStream[iCurrPos])                         // 字符串最后一个字符为0{m_iCurrPos = iCurrPos;break;}iCurrPos++;}// 判断是否合法if(m_iCurrPos < m_iStreamSize){m_iCurrPos++;                                       // skip 0return psz;}assert(FALSE);                                         // 越界断言return NULL;
}

如何用C++封装一个简单的数据流操作类(附源码),从而用于网络上的数据传输和解析?相关推荐

  1. java计算机毕业设计vue开发一个简单音乐播放器(附源码、数据库)

    java计算机毕业设计vue开发一个简单音乐播放器(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ec ...

  2. Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)

    本文内容: 1.横向ListView的所有实现思路; 2.其中一个最通用的思路HorizontalListView,并基于横向ListView开发一个简单的相册: 3.实现的横向ListView在点击 ...

  3. 用C语言easyx库来写一个简单的翻翻乐小游戏(附源码素材)

    简明目录 写在前面 easyx库 准备工作 新建项目文件 分析 素材分析 上代码吧 地图表示 开始界面 地图初始化(打乱) 游戏过程实现 主函数的实现 测试 优化 1.游戏分数 2.游戏时间 3.nu ...

  4. 如何用CSS写一个缺角的div(附源码)

    今天用CSS写一个缺角的div,大致的效果是这样的,div的左上角和右上角缺掉了一块的效果,简称为缺角div <!DOCTYPE html> <html lang="en& ...

  5. 一个简单漂亮的网址导航HTML5源码

    正文: 一个简单漂亮的网址导航HTML5源码页面自适应,手机电脑都自动适应大小. 纯HTML代码,然后一个CSS一个JS文件,根据设备自适应,更多信息自行研究,修改index.html内容. 字节网盘 ...

  6. 基于JAVAvue开发一个简单音乐播放器计算机毕业设计源码+数据库+lw文档+系统+部署

    基于JAVAvue开发一个简单音乐播放器计算机毕业设计源码+数据库+lw文档+系统+部署 基于JAVAvue开发一个简单音乐播放器计算机毕业设计源码+数据库+lw文档+系统+部署 本源码技术栈: 项目 ...

  7. vc++6.0:MFC写的一个汉字取模软件(附源码)

    vc++6.0:MFC写的一个汉字取模软件(附源码) 一.毕业有九年了,突然想起以前在大学时候用MFC写的一个汉字取模软件.主要的功能是在PC上获取汉字16X16点阵数据然后通过串口把点阵数据发送给A ...

  8. 使用Python 封装一个简单的Mysql工具类

    pymysql操作mysql,虽然简单,但每次都要链接数据库,获取游标,关闭游标,关闭链接.这些操作无技术含量,还要重复编写!!想一想不如封装一个DBUtil,来提高开发效率. 要编写工具类首先要把公 ...

  9. 自己写php模板引擎,如何用php编写一个简单的模板引擎(附代码)

    php web开发中广泛采取mvc的设计模式,controller传递给view层的数据,必须通过模板引擎才能解析出来.实现一个简单的仅仅包含if,foreach标签,解析$foo变量的模板引擎. 编 ...

最新文章

  1. Matlab并行编程方法
  2. 不同技术团队的配合问题及DevOps
  3. GitHub开源项目:用于构建接入网络和模块化网络服务的平台
  4. .net core精彩实例分享 -- 异步和并行
  5. mysql语法错误文件_使用logstash同步MySQL的数据时,在jdbc查询sql文件时报sql语法错误,sql文件是navicat生成的...
  6. java中继承applet类_Java - 33 Java Applet基础
  7. Flink:flink问题总结
  8. 一些图像置乱算法matlab
  9. 网页加速系列(六)、 网页加速之进阶下篇
  10. windows下GMT绘制矢量图
  11. 网站自动SEO优化软件
  12. 【书单推荐】西方思想史上占有重要地位的100部思想名著
  13. torch.nn.MSELoss的用法
  14. 【整理】MFC单文档程序窗口大小的设置
  15. 《感动中国》2005年度人物评选揭晓
  16. python xgb模型 预测_如何使用XGBoost模型进行时间序列预测
  17. 非劣效性 等效性 有效性临床试验
  18. selenium自动化测试实战教学(12306自动化订票)春节出行必备
  19. Elasticsearch 性能优化指引(十八)
  20. P2P网贷平台运营必知

热门文章

  1. java中的 的意思_java中 的意思是什么意思是什么意思是什么意思
  2. mysql无法连接内网的数据库
  3. Spring实战之Spring动态加载jar模块和卸载模块
  4. 为什么注销电话卡非要去营业厅
  5. python计算iris数据集的均值_模糊C均值聚类算法及python实现
  6. 2022年湖北武汉八大员考试报名详细介绍,甘建二
  7. 建筑八大员培训湖北标准员培训施工现场质量标准化的建设措施
  8. 我叫mt4公会攻城战服务器维护中,我叫mt4公会攻城战怎么玩 公会攻城战玩法介绍...
  9. 网易云信短信对接-php
  10. 【threejs】实现星空、星链、太阳系、线等效果的参考