因爲歷史緣由,咱們目前看到的大部分的網絡協議都是基於ASCII碼這種純文本方式,也就是基於字符串的命令行方式,好比HTTP、FTP、POP三、SMTP、Telnet等。早期操做系統UNIX(或DOS),用戶操做界面就是控制檯,控制檯的輸入輸出方式就決定了用戶只能經過敲擊鍵盤的方式將協議命令輸入到網絡,這也就致使了回車換行"\r\n"會做爲一次命令結束的標識。html
好比HTTP協議,與主機創建鏈接後,輸入"GET / HTTP/1.1\r\n"便可獲取網站的主頁。web
好比email協議,早期的電子郵件協議只支持ASCII碼這種純文本傳輸,但隨着全世界人民對物質文化生活的不斷嚮往,這種落後的傳輸方式,已經沒法知足世界人民對美好生活的追求,好比圖像、視頻、音頻、Office文件如何在郵件中展示?不一樣國家(非英語國家)字符集該如何傳輸和展示?windows
換句話說,就是這種非ASCII的二進制富文本,該如何傳輸和呈現?緩存
此時MIME標準誕生了,MIME的出現更多的是一種向下兼容的無奈,而不是革命。經過對二進制數據或非ASCII碼數據進行base64或quoted-printable編碼,來實現純ASCII碼的傳輸。顯然這種方式會讓你的郵件體變大,傳輸效率降低。尤爲附件不少時,經過MIME的boundary來解析郵件的附件也是一筆額外的負擔。服務器
同時MIME的標準也被HTTP協議所採用,咱們能夠經過content-type指定傳輸的內容是什麼類型,經過MIME的boundary來對Form-Data數據進行擴展,讓咱們Post數據時也可以在「表格」數據中插入文件,從而達到上傳文件的效果。websocket
顯然這種方式不如二進制簡潔,但卻很是的直觀,所見即所得,一眼就能看明白。但就傳輸效率上不如二進制方式。網絡
又好比websocket協議雖然創建會話時採用的是HTTP協議,但後續的數據幀格式倒是一個二進制格式。以下:
socket
在這種格式下,爲了表示每幀數據長度,就必定會有一個「數據長度」項,好比上面的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() { // 一、測試數據流,寫入數據 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); // 二、測試數據流,重載運算符(<<,>>)的測試 // 測試運算符重載 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); }
輸出結果