sql server先後端通訊的數據包的格式,能夠大體分爲:公共頭部+個性化包頭部+數據。其中公共頭部是包的最開始的8byte的內容,每一個包都有這8byte的內容,含義相同。再緊接着是各類類型的數據包的頭部,這個就須要根據各類包的不一樣,這部分的內容的含義也不一樣。最後是數據,有些包可能沒有數據。sql
公共頭部是包最前面的8個字節,這8個字節的含義爲:後端
第一個字節:包類型,根據msdn的介紹,目前有11種包的類型再使用。本文須要介紹的sql batch包的類型爲0x01.服務器
第二字節:包的狀態,根據msdn的介紹,包的狀態有5中取值(0x00, 0x01, 0x02, 0x08, 0x10)。其中0x00表示普通包,0x01表示當前包是最後一個包。sqlserver
第三,四個字節:包的長度。包的長度是通過2個byte來描述的,而且採用大端序的方式。後面能夠了解到sql server中包中的數據即包括小端序的數據又包括大端序的數據。此處是大端序的數據。包的長度包括頭部的8字節長度。spa
第五,六字節:包的spid。根據msdn的解釋,這個字段是保存的是服務器的進程ID的編號。code
第七字節:包的序號。包的序號取值在1-256,經過這個序號能夠把打包拆分爲小包進行發送到服務端或者服務端發送到客戶端。server
第八字節:站位字節(pad)。目前沒有使用,一般設置爲0x00。這個字段應該被接收者忽略掉。進程
在發送sqlbatch包的時候,除了公共的頭部外,還要sqlbatch包的頭部信息。sqlbatch包的頭部包括:ip
全部頭部的總長度,後面依次是每一個頭部的長度,類型,數據。下面是是msdn官網的一個例子的一部分狀況:unicode
<All_HEADERS> <TotalLength> <DWORD>16 00 00 00 </DWORD> </TotalLength> <Header> <HeaderLength> <DWORD>12 00 00 00 </DWORD> </HeaderLength> <HeaderType> <USHORT>02 00 </USHORT> </HeaderType> <HeaderData> <MARS> <TransactionDescriptor> <ULONGLONG>00 00 00 00 00 00 00 01 </ULONGLONG> </TransactionDescriptor> <OutstandingRequestCount> <DWORD>00 00 00 00 </DWORD> </OutstandingRequestCount> </MARS> </HeaderData> </Header> </All_HEADERS>
全部頭部長度站位4byte。其中頭部長度佔4byte,類型佔2byte。其中全部頭部的長度包括自身長度+全部頭部的長度。從上面的例子中也能夠注意到包的長度,包類型都是小端序的。
包數據的長度=公共頭部中的長度-sqlbatch包的頭部長度-8個字節的公共長度。獲得包數據的長度後,就能夠讀取到sql text的數據。可是讀取到的數據是unicode的,須要進行轉換後才能獲得咱們須要的sql語句。下面是從Unicode進行轉換的代碼:
int Tool::unicode2string(const char* unicodeStr, const int len, std::string& desStr) { if (len%2 != 0) { return -1; } desStr.reserve(len/2+1); int i = 0; for (i = 0; i < len; i = i + 2) { desStr.insert(i/2, 1, (char)((unicodeStr[i] & 0xff) | (unicodeStr[i+1] & 0xff) << 8)); } desStr.insert(len/2, 1, (char)'\0'); return 0; }
則經過上面的瞭解後,須要提取sql batch包中的sql語句仍是比較簡單的。基本過程以下所示:
1. 解析公共頭部,獲得整個包的長度
2. 解析獲得sqlbatch的頭部長度
3. 計算獲得sql語句的開始位置以及長度
4. 讀取到Unicode類型的sql語句,再調用unicode2string來進行轉換。
更多信息,請期待oneproxy-for-sqlserver.