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 }