wireshark 是一款网络抓包工具,能够让咱们很方便的查看mysql协议。html
网络字节序分为大端法和小端法,它们的区别能够参考其它网站,在此不作叙述。mysql
golang是一款很是赞的开发语言,学习成本比较低,并发性能好的特色golang
mysql 协议文档详见:(http://www.cnblogs.com/davygeek/p/5647175.html)sql
mysql 协议分为报文头和报文体
服务器
这里为了简单起见,没有处理任何的异常状况网络
package main import ( "bytes" "fmt" "io" "net" ) type Handshake struct { //1Byte 协议版本号 protocol byte //n Byte 服务器版本信息(Null-Termimated-String) version string //4Byte服务器线程ID theadID int //8bytes 随机挑战数 salt1 string //1Byte 填充值(0x00) fill uint8 //2Byte 服务器权能标志(低16位) capabilitiesLow int //1Byte字符编码 charset uint8 //2Byte服务器状态 status int //2Byte 服务器权能标志(高16位) capabilitiesHigh int //随机挑战数(12位) salt2 string // pluginName string } const ( ipAddr = "127.0.0.1" port = "3306" ) func main() { conn, _ := net.Dial("tcp", fmt.Sprintf("%s:%s", ipAddr, port)) defer conn.Close() //读取消息体 body, _ := ReadPackge(conn) r := bytes.NewReader(body) //跳过4Byte消息头 SkipHeaderPackage(r) //读取1Byte协议版本号 protoVersion, _ := r.ReadByte() //n Byte服务版本信息 serverVersion := ReadNullTerminatedString(r) //4 byte 服务线程ID theadIDBuf := make([]byte, 4) r.Read(theadIDBuf) theadId := int(uint32(theadIDBuf[0]) | uint32(theadIDBuf[1])<<8 | uint32(theadIDBuf[2])<<16 | uint32(theadIDBuf[3])<<24) //8 byte 随机挑战数 saltBuf := make([]byte, 8) salt, _ := r.Read(saltBuf) //跳过 1 byte填充值 r.Seek(1, io.SeekCurrent) //读取 2 byte 服务器权能标识(低16位) capabilitiesLow1Buf := make([]byte, 2) r.Read(capabilitiesLow1Buf) capabilitiesLow := int(uint32(capabilitiesLow1Buf[0]) | uint32(capabilitiesLow1Buf[1])<<8) //读取1 byte 字符编码 chartset, _ := r.ReadByte() //读取2 byte 服务器状态 serverStatusBuf := make([]byte, 2) r.Read(serverStatusBuf) serverStatus := int(uint32(serverStatusBuf[0]) | uint32(serverStatusBuf[1])) //读取服务器权能标志(高16位) capabilitiesHigh2Buf := make([]byte, 2) r.Read(capabilitiesHigh2Buf) capabilitiesHigh := int(uint32(capabilitiesHigh2Buf[0]) | uint32(capabilitiesHigh2Buf[1])<<8) //跳过 1byte 未使用的挑战长度 r.Seek(1, io.SeekCurrent) //跳过10 byte 填充值 r.Seek(10, io.SeekCurrent) //读取12位挑战随机数 salt2 := ReadNullTerminatedString(r) //读取密码认证插件 pluginName := ReadNullTerminatedString(r) //handshake handshake := Handshake{ protocol: protoVersion, version: serverVersion, theadID: theadId, salt1: string(salt), capabilitiesLow: capabilitiesLow, charset: chartset, status: serverStatus, capabilitiesHigh: capabilitiesHigh, salt2: salt2, pluginName: pluginName, } //打印输出 fmt.Println(handshake) } //读取已0x00结尾的字符串 func ReadNullTerminatedString(r *bytes.Reader) string { var str []byte for { b, _ := r.ReadByte() if b == 0x00 { return string(str) } else { str = append(str, b) } } } //跳过消息头 func SkipHeaderPackage(r *bytes.Reader) error { if _, err := r.Seek(4, io.SeekStart); err != nil { return err } return nil } //读取数据包 func ReadPackge(conn net.Conn) ([]byte, error) { //读取4byte消息头 header := []byte{0, 0, 0, 0} if _, err := io.ReadFull(conn, header); err != nil { return nil, err } //获取3byte消息长度 bodyLen := int(uint32(header[0]) | uint32(header[1]<<8) | uint32(header[2]<<16)) body := make([]byte, bodyLen) //读取消息体 n, err := io.ReadFull(conn, body) if err != nil { return nil, err } return append(header, body[0:n]...), nil }