歷史溯源
由於歷史原因,我們目前看到的大部分的網絡協議都是基於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();
	// 關聯一塊stream
	void 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();
	// 讀寫WORD
	void WriteWord(WORD wValue);
	WORD ReadWord();
	// 讀寫DWORD
	void WriteDWord(DWORD dwValue);
	DWORD ReadDWord();
	// 讀寫int64
	void WriteInt64(__int64 i64Value);
	__int64 ReadInt64();
	// 讀寫Float
	void WriteFloat(float fValue);
	float ReadFloat();
	// 讀寫double
	void 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;		// 數據流是否為網絡字節序,缺省為FALSE
	BYTE *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;
	else
		m_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;
	else
		m_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;						// 末尾0
	assert(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 0
		return psz;
	}
	assert(FALSE);											// 越界斷言
	return NULL;
}
 
        測試代碼
void TestDataStream()
{
	// 1、測試數據流,寫入數據
	BYTE szBuff[1024] = {0};
	CDataStream ds;
	ds.Attach(szBuff, 1024);
	ds.WriteByte(1);
	ds.WriteWord(2);
	ds.WriteDWord(1000);
	ds.WriteInt64(5678);
	ds.WriteData((BYTE*)"ASDF\0", 5);	
	ds.WriteFloat(1234567890.12f);
	ds.WriteDouble(1234567890.123);
	ds.WriteString("Hello word!");
	// 讀取數據流
	ds.Reset();		// 指向流的頭
	BYTE byValue = ds.ReadByte();
	WORD wValue = ds.ReadWord();
	DWORD dwValue = ds.ReadDWord();
	__int64 i64Value = ds.ReadInt64();
	BYTE* pData = ds.ReadData(5);
	float fValue = ds.ReadFloat();
	double dValue = ds.ReadDouble();
	const char* psz = ds.ReadString();
	printf("CDataStream讀寫測試:\r\n");
	printf("BYTE=%d, WORD=%d, DWORD = %d, INT64 = %I64u, FLOAT = %f, DOUBLE = %f, %s\r\n", 
			byValue, wValue, dwValue, i64Value, fValue, dValue, psz);
	printf("pData = %s\r\n", (char*)pData);
	// 2、測試數據流,重載運算符(<<,>>)的測試
	// 測試運算符重載
	CDataStream dds;
	dds.Attach(szBuff, 1024);
	BYTE byRet = 0;
	WORD wRet = 0;
	DWORD dwRet = 0;
	__int64 i64Ret = 0;
	float fRet = 0;
	double dRet = 0;
	char* pszRet;
	dds << (BYTE)1 << (WORD)2 << (DWORD)3 << (__int64)100 << 30.1f << 128.12 << "Hello word!";
	dds.Reset();
	dds >> byRet >> wRet >> dwRet >> i64Ret >> fRet >> dRet >> pszRet;
	printf("CDataStream測試,運算符重載:\r\n");
	printf("by1 = %d, WORD = %d, DWORD = %d, INT64 = %I64u, FLOAT = %f, DOUBLE = %f, %s\r\n", 
					byRet, wRet, dwRet, i64Ret, fRet, dRet, pszRet);
}
 
        輸出結果
 
