您的位置:首页 > 数据库

SQL server jdbc之prelogin数据包

2016-07-08 00:00 267 查看
为了做sql server的监控功能,需要了解SQL SERVER jdbc与SQL SERVER之间的通信协议。目前在Windows上面安装了sqlserver2016的developer版本。并且通过jdbc来操作数据库,可以得到jdbc的日志情况。通过日志可以了解到通信的过程,从通信的过程可以看到jdbc先发送了一个prelogin数据包到服务端。下面通过反编译的jdbc代码来学习prelogin包的内容。

prelogin数据包是在SQLServerConnection.class文件的Prelogin函数中进行构建的。与prelogin数据包相关的内容如下所示:

[code=language-java]byte[] arrayOfByte1 = { 18, 1, 0, 67, 0, 0, 0, 0, 0, 0, 16, 0, 6, 1, 0, 22, 0, 1, 5, 0, 23, 0, 36, -1, 0, 0, 0, 0, 0, 0, this.requestedEncryptionLevel, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
......
ActivityId localActivityId = ActivityCorrelator.getNext();
byte[] arrayOfByte3 = Util.asGuidByteArray(localActivityId.getId());
byte[] arrayOfByte4 = Util.asGuidByteArray(this.clientConnectionId);
int i = arrayOfByte1.length - 36;

System.arraycopy(arrayOfByte4, 0, arrayOfByte1, i, arrayOfByte4.length);
i += arrayOfByte4.length;

System.arraycopy(arrayOfByte3, 0, arrayOfByte1, i, arrayOfByte3.length);
i += arrayOfByte3.length;
long l = localActivityId.getSequence();
Util.writeInt((int)l, arrayOfByte1, i);
i += 4;
......
try
{
this.tdsChannel.write(arrayOfByte1, 0, arrayOfByte1.length);
this.tdsChannel.flush();
}
......

上面去掉了无关代码后,其中arrayOfByte1数组中的内容为prelogin数据包的初始内容。再在初始内容的基础上面通过初步计算得到结果需要发送的数据包内容,修改的数据过程如下所示:

1. 把clientConnectionId的内容复制到arrayOfByte1数组中

2. 把localActivityId的id内容复制到arrayOfByte1数组中

3.把localActivityId的Sequence写到arrayOfByte1数组中,Sequence的长度为4个字节。

通过上面的修改后,把arrayOfByte1中的内容通过tdsChannel的write函数发送出去。

现在需要弄清楚的是requestedEncryptionLevel,clientConnectionId,localActivityId.id, localActivityId.Sequence值得来源或者计算过程。

1. requestedEncryptionLevel

这个参数的赋值是在SQLServerConnection.java文件的connect函数中,具体代码如下所示:

[code=language-java]str2 = SQLServerDriverBooleanProperty.ENCRYPT.toString();
str3 = this.activeConnectionProperties.getProperty(str2);
if (str3 == null)
{
str3 = Boolean.toString(SQLServerDriverBooleanProperty.ENCRYPT.getDefa
this.activeConnectionProperties.setProperty(str2, str3);
}

this.requestedEncryptionLevel = (booleanPropertyOn(str2, str3) ? 1 : 0);

str2的值为encrypt,str3的值是根据参数进行取值,默认情况下是false。于是可以得到当连接参数中没有设置encrypt时,requestedEncryptionLevel的值为0.否则为1.

2. clientConnectionId

这个参数的赋值是在SQLServerConnection.java文件的connectHelper函数中,具体代码如下所示:

[code=language-java]this.clientConnectionId = UUID.randomUUID();
assert (null != this.clientConnectionId);

clientConnectionId是通过UUID.randomUUID函数包装的。我机器上面jdbc打印出来的ID为ClientConnectionId: c4000772-d238-4feb-a388-fa63320b03e7。

3. localActivityId.id

从ActivityId.java文件中可以看到id也是一个UUID。取值的方法也是通过调用UUID.randomUUID函数来获得。具体的代码如下所示:

[code=language-java]ActivityId()
{
this.Id = UUID.randomUUID();
this.Sequence = 0L;
this.isSentToServer = false;
}

id是在ActivityId的构造函数中进行初始化的。

4. localActivityId.Sequence

Sequence的值是通过+1来计算的。计算的函数如下所示:

[code=language-java]void Increment()
{
if (this.Sequence < 4294967295L)
{
this.Sequence += 1L;
}
else
{
this.Sequence = 0L;
}

this.isSentToServer = false;
}

Sequence的长度是long型,所以长度不能超过4字节(FFFFFFFF)。

故通过上面的计算后能够得到prelogin的最终数据包格式是:

[code=plain]12 01 00 43 00 00 00 00 00 00 10 00 06 01 00 16   ...C............
00 01 05 00 17 00 24 FF 00 00 00 00 00 00 00 72   ......$........r
07 00 C4 38 D2 EB 4F A3 88 FA 63 32 0B 03 E7 C4   ...8..O...c2....
7D 75 88 A5 79 E5 4F B2 D7 23 35 0E A9 2B DB 01   }u..y.O..#5..+..
00 00 00

这个数据包的内容是来做jdbc的连接日志,是最终的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: