日期:2014-05-16  浏览次数:20617 次

H2数据库client-server协议全解(草稿-不定期补充)

?

H2数据库的版本: 1.3.169

?

server端有一个listen线程,

org.h2.server.TcpServer.listen()

?

此线程一直在监听客户端连接,

一但收到客户端连接请求就开一个新的线程处理它(并未使用线程池、每连接每线程)

?

?

协议共有18条命令(从0到17)

?

?

1. 初始化阶段(或握手阶段)

?

client先发数据:

int     minClientVersion
int     maxClientVersion
String  db               //数据库名
String  URL
String  userName
byte[]  userPasswordHash //32字节,使用SHA256算法对 userName + "@" + userPassword 进行hash,作为用户保存到数据库中的密码
byte[]  filePasswordHash //32字节,使用SHA256算法对 "file@" + filePassword 进行hash,用来加密数据库文件
int     keys length //url中的key-value参数个数

keys length个
{
	String key
	String value
}
?

?

当前支持的协议版本是6、7、8、9、10、11、12

?

minClientVersion和maxClientVersion用来告诉server端当前client能支持的最小和最大协议版本是多少,

根据这两个参数,server端会选择一个合适的协议版本与client通信,

选择合适的协议版本的方法如下:

6<=minClientVersion<=12,

如果maxClientVersion>=12,那么就用12,否则使用minClientVersion

?

?

server响应: STATUS_OK 或 STATUS_ERROR

?

STATUS_OK:
------------------------------------
int     STATUS_OK(1)
int     clientVersion
------------------------------------


STATUS_ERROR: 
------------------------------------
int     STATUS_ERROR(0)
String  SQLState value
String  message
String  sql
int     errorCode 
String  trace
------------------------------------
见org.h2.server.TcpServerThread.sendError(Throwable)
?

?

?

2. sessionId设置

?

client自己生成一个长度为64个字符的sessionId,然后传给server,

client给server发送的是一个SESSION_SET_ID数据包

?

sessionId在server端会在内存中保存,当client调用org.h2.jdbc.JdbcStatement.cancel()时才用到

?

------------------------------------

int ? ? SESSION_SET_ID(12)

String ?sessionId

------------------------------------

?

server响应: 只有STATUS_OK

------------------------------------

int ? ? STATUS_OK(1)

------------------------------------

?

?

上面的1和2在client和server之间每次建立一个新的connection(或称为session)时都会固定发送,

(见: org.h2.engine.SessionRemote.initTransfer(ConnectionInfo, String, String))

?

?

?

?

3. prepare阶段

?

?

接下来按执行SQL语句的类别分成两类: QUERY和UPDATE

?

执行SQL之前,需要先进行prepare,

client给server发送的是一个SESSION_PREPARE_READ_PARAMS或SESSION_PREPARE数据包,两者格式几本上一样,

SESSION_PREPARE_READ_PARAMS需要server响应的数据包中包含SQL语句中的参数元数据,SESSION_PREPARE则不需要,

SESSION_PREPARE用在第二次对同一个SQL进行prepare时。

?

SESSION_PREPARE_READ_PARAMS

------------------------------------

int ? ? SESSION_PREPARE_READ_PARAMS(11)

int ? ? id(执行当前sql时为此操作生成的一个计数器)

String ?sql

------------------------------------

?

server响应:

------------------------------------

int ? ? STATUS_OK 或 STATUS_OK_STATE_CHANGED

boolean isQuery

boolean readonly

int ? ? sql parameter size

Parameter MetaData[]

------------------------------------

?

SESSION_PREPARE

------------------------------------

int ? ? SESSION_PREPARE(0)

int ? ? id(执行当前sql时为此操作生成的一个计数器)

String ?sql

------------------------------------

?

server收到上面两个包后,会按id把对应的SQL缓存起来,

server响应:

------------------------------------

int ? ? STATUS_OK 或 STATUS_OK_STATE_CHANGED

boolean isQuery

boolean readonly

int ? ? sql parameter size

------------------------------------

?

相关源代码见: org.h2.command.CommandRemote.prepare(SessionRemote, boolean)

?

?

?

4. update

?

当进行java.sql.Statement.executeUpdate之类的操作时通常会触发update

?

COMMAND_EXECUTE_UPDATE

------------------------------------

int ? ? COMMAND_EXECUTE_UPDATE(3)

int ? ? id(对应prepare阶段生