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.