├── README.md ├── client.go ├── protocol.go └── stpro.go /README.md: -------------------------------------------------------------------------------- 1 | # stpro 一个基于tcp协议实现的简洁通信框架 2 | # a skeleton for communication based on TCP 3 | 4 | ## 特性 5 | 6 | * 引入go包即可使用 7 | * 实现了crc校验,保证数据传输的完整性与正确性 8 | * 调用方式简单明了 9 | 10 | ## 快速开始 11 | 12 | ### 1. 引入 13 | 14 | ``` 15 | import "stpro" 16 | 17 | ``` 18 | ### 2. server 端 19 | 20 | ``` 21 | /** 三步搭建服务端 22 | 1 定义任意名称struct的数据结构,必须包含Pmap、Phost两个 23 | 字段,其中Phost为服务端ip+port拼接的字符串,Pmap为自定 24 | 义数据包类型与数据包名称的映射。 25 | 2 实例化对象为字段赋值,实现对应已定义`包名称`的数据包处 26 | 理方法,方法名必为"P[包名称]",如type包的处理方法为Ptype 27 | 。方法中请定义数据处理逻辑,输入输入皆为[]byte类型。 28 | 3 stpro.New()传入实例化的对象,如无报错则服务端开始监听, 29 | 并按照你所定义的逻辑处理数据包,返回响应数据。 30 | **/ 31 | package main 32 | 33 | import ( 34 | "fmt" 35 | "stpro" 36 | ) 37 | 38 | type Server struct { 39 | Phost string 40 | Pmap map[uint8]string 41 | } 42 | 43 | func (m Server) Ptype(in []byte) (out []byte) { 44 | fmt.Printf("客户端发来type包:%s\n", in) 45 | /** process... **/ 46 | bytes := []byte("hello1") 47 | return bytes 48 | } 49 | 50 | func (m Server) Pname(in []byte) (out []byte) { 51 | fmt.Printf("客户端发来name包:%s\n", in) 52 | /** process... **/ 53 | bytes := []byte("hello2") 54 | return bytes 55 | } 56 | 57 | func main() { 58 | m := Model{ 59 | Phost: ":9091", 60 | Pmap: make(map[uint8]string), 61 | } 62 | m.Pmap[0x01] = "type" 63 | m.Pmap[0x02] = "name" 64 | err := stpro.New(m) 65 | if err != nil { 66 | fmt.Println(err) 67 | } 68 | } 69 | 70 | ``` 71 | 72 | ### 3.client端 73 | 74 | ``` 75 | /** 76 | 三部搭建客户端 77 | 1 数据结构同服务端。 78 | 2 P[type]方法是发送对应包后接收到响应数据的处理方法。 79 | 3 实例化对象,并调用Send(type byte, content []byte)方 80 | 法发送数据到客户端,接收到的数据后会自定按照上述定 81 | 义方法处理。 82 | **/ 83 | package main 84 | 85 | import ( 86 | "fmt" 87 | "stpro" 88 | ) 89 | 90 | type Client struct { 91 | Phost string 92 | Pmap map[byte]string 93 | } 94 | 95 | func (c Client) Ptype(in []byte) { 96 | fmt.Printf("收到了type包的回复:%s\n", in) 97 | } 98 | 99 | func (c Client) Pname(in []byte) { 100 | fmt.Printf("收到了name包的回复:%s\n", in) 101 | } 102 | 103 | func main() { 104 | client, err := stpro.NewClient(Client{ 105 | Phost: "192.168.1.106:9091", 106 | Pmap: map[byte]string{ 107 | 0x01: "type", 108 | 0x02: "name", 109 | }, 110 | }) 111 | 112 | if err != nil { 113 | fmt.Println(err) 114 | return 115 | } 116 | 117 | err = client.Send(0x02, []byte("jintianzhenhao")) 118 | if err != nil { 119 | fmt.Println(err) 120 | return 121 | } 122 | 123 | err = client.Send(0x01, []byte("jintianzhenhao3333")) 124 | if err != nil { 125 | fmt.Println(err) 126 | return 127 | } 128 | } 129 | ``` 130 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | /** stpro v1.0 **/ 2 | /** The implementation of a simple protocol based on TCP.**/ 3 | /** Copyright 2017. Author:by-zhang@github.com **/ 4 | package stpro 5 | 6 | import ( 7 | "bufio" 8 | "errors" 9 | "net" 10 | "reflect" 11 | ) 12 | 13 | type Client struct { 14 | RAddr *net.TCPAddr 15 | Conn *net.TCPConn 16 | } 17 | 18 | var enPacketedData []byte 19 | var enPacketChan = make(chan bool) 20 | 21 | func NewClient(v interface{}) (Client, error) { 22 | vTyp = reflect.TypeOf(v) 23 | vVal = reflect.ValueOf(v) 24 | cli := Client{} 25 | err := cli.checkField() 26 | if err != nil { 27 | return cli, err 28 | } 29 | err = cli.checkMethod() 30 | if err != nil { 31 | return cli, err 32 | } 33 | return cli, nil 34 | } 35 | 36 | func (cli *Client) checkField() error { 37 | Addr, err := commonFieldCheck() 38 | if err != nil { 39 | return err 40 | } 41 | cli.RAddr = Addr 42 | return nil 43 | } 44 | 45 | func (cli *Client) checkMethod() error { 46 | err := commonMethodCheck() 47 | if err != nil { 48 | return err 49 | } 50 | for i := 0; i < moduleNum; i++ { 51 | tempMethod := vTyp.Method(i) 52 | if moduleNames[tempMethod.Name[1:]] == nil { 53 | err := errors.New("init error: methods are defined incorrectly.") 54 | return err 55 | } 56 | if tempMethod.Type.In(1) != byteSliceType { 57 | err := errors.New("init error: method `" + tempMethod.Name + "` has wrong type of params.") 58 | return err 59 | } 60 | mapNameFunc[tempMethod.Name[1:]] = tempMethod.Func 61 | } 62 | return nil 63 | } 64 | 65 | func (cli *Client) dial() error { 66 | if cli.Conn == nil { 67 | conn, err := net.DialTCP("tcp", nil, cli.RAddr) 68 | if err != nil { 69 | return err 70 | } 71 | cli.Conn = conn 72 | } 73 | return nil 74 | } 75 | 76 | func (cli *Client) Send(typ byte, data []byte) error { 77 | go cli.enPacket(typ, data) 78 | err := cli.dial() 79 | <-enPacketChan 80 | if err != nil { 81 | return err 82 | } 83 | cli.send() 84 | finishReply := make(chan bool) 85 | taskChan := make(chan []byte, 1) 86 | go cli.Process(taskChan, finishReply) 87 | go dePacket(cli.Conn, &taskChan) 88 | <-finishReply 89 | return nil 90 | } 91 | 92 | func (cli *Client) Process(taskChan chan []byte, finishReply chan bool) { 93 | for recvBuffer := range taskChan { 94 | packetType := recvBuffer[0] 95 | data := recvBuffer[1:] 96 | mapNameFunc[originMap[packetType]].Call([]reflect.Value{vVal, reflect.ValueOf(data)}) 97 | finishReply <- true 98 | } 99 | } 100 | 101 | func (cli *Client) enPacket(typ byte, data []byte) { 102 | enPacketedData = enPacket(typ, data) 103 | enPacketChan <- true 104 | } 105 | 106 | func (cli *Client) send() { 107 | bufferWriter := bufio.NewWriter(cli.Conn) 108 | bufferWriter.Write(enPacketedData) 109 | bufferWriter.Flush() 110 | } 111 | -------------------------------------------------------------------------------- /protocol.go: -------------------------------------------------------------------------------- 1 | /** stpro v1.0 **/ 2 | /** The implementation of a simple protocol based on TCP.**/ 3 | /** Copyright 2017. Author:by-zhang@github.com **/ 4 | package stpro 5 | 6 | import ( 7 | "bufio" 8 | "errors" 9 | "fmt" 10 | "hash/crc32" 11 | "io" 12 | "net" 13 | ) 14 | 15 | func dePacket(conn net.Conn, taskChan *chan []byte) { 16 | state := 0x00 17 | length := uint16(0) 18 | crc16 := uint16(0) 19 | var recvBuffer []byte 20 | cursor := uint16(0) 21 | reader := bufio.NewReader(conn) 22 | for { 23 | recvByte, err := reader.ReadByte() 24 | if err != nil { 25 | if err == io.EOF { 26 | fmt.Printf("closed: tcp client %s\n", conn.RemoteAddr().String()) 27 | close(*taskChan) 28 | return 29 | } else { 30 | continue 31 | } 32 | 33 | } 34 | switch state { 35 | case 0x00: 36 | if recvByte == 0xFF { 37 | state = 0x01 38 | } 39 | case 0x01: 40 | if recvByte == 0xFF { 41 | state++ 42 | } else { 43 | state = 0x00 44 | } 45 | case 0x02: 46 | length += uint16(recvByte) * 256 47 | state++ 48 | case 0x03: 49 | length += uint16(recvByte) 50 | recvBuffer = make([]byte, length) 51 | state++ 52 | case 0x04: 53 | recvBuffer[cursor] = recvByte 54 | cursor++ 55 | if cursor == length { 56 | state++ 57 | } 58 | case 0x05: 59 | crc16 += uint16(recvByte) * 256 60 | state++ 61 | case 0x06: 62 | crc16 += uint16(recvByte) 63 | state++ 64 | case 0x07: 65 | if recvByte == 0xFF { 66 | state++ 67 | } else { 68 | state = 0x00 69 | } 70 | case 0x08: 71 | if recvByte == 0xFE { 72 | err := checkDataCrc(recvBuffer, crc16) 73 | if err == nil { 74 | *taskChan <- recvBuffer 75 | } 76 | } 77 | state = 0x00 78 | length = uint16(0) 79 | crc16 = uint16(0) 80 | cursor = uint16(0) 81 | recvBuffer = nil 82 | } 83 | } 84 | } 85 | 86 | func checkDataCrc(buffer []byte, crc16 uint16) (err error) { 87 | if (crc32.ChecksumIEEE(buffer)>>16)&0xFFFF == uint32(crc16) { 88 | err = nil 89 | } else { 90 | err = errors.New("err:data check failed") 91 | } 92 | return 93 | } 94 | 95 | func enPacket(packetType byte, sendBytes []byte) []byte { 96 | tempSlice := make([]byte, len(sendBytes)+1) 97 | tempSlice[0] = packetType 98 | copy(tempSlice[1:], sendBytes) 99 | packetLength := len(tempSlice) + 8 100 | result := make([]byte, packetLength) 101 | result[0] = 0xFF 102 | result[1] = 0xFF 103 | result[2] = byte(uint16(len(tempSlice)) >> 8) 104 | result[3] = byte(uint16(len(tempSlice)) & 0xFF) 105 | copy(result[4:], tempSlice) 106 | sendCrc := crc32.ChecksumIEEE(tempSlice) 107 | result[packetLength-4] = byte(sendCrc >> 24) 108 | result[packetLength-3] = byte(sendCrc >> 16 & 0xFF) 109 | result[packetLength-2] = 0xFF 110 | result[packetLength-1] = 0xFE 111 | return result 112 | } 113 | -------------------------------------------------------------------------------- /stpro.go: -------------------------------------------------------------------------------- 1 | /** stpro v1.0 **/ 2 | /** The implementation of a simple protocol based on TCP.**/ 3 | /** Copyright 2017. Author:by-zhang@github.com **/ 4 | package stpro 5 | 6 | import ( 7 | "bufio" 8 | "errors" 9 | "fmt" 10 | "net" 11 | "reflect" 12 | "strings" 13 | ) 14 | 15 | const ( 16 | MAP_NAME = "Pmap" 17 | HOST_NAME = "Phost" 18 | ) 19 | 20 | type Server struct { 21 | LAddr *net.TCPAddr 22 | Listener *net.TCPListener 23 | } 24 | 25 | var ( 26 | vTyp = *new(reflect.Type) 27 | vVal = *new(reflect.Value) 28 | mapTypeString = reflect.TypeOf(new(map[byte]string)).Elem().String() 29 | byteSliceType = reflect.TypeOf(new([]byte)).Elem() 30 | moduleNum = 0 31 | originMap = map[byte]string{} 32 | moduleNames = map[string]*byte{} 33 | mapNameFunc = map[string]reflect.Value{} 34 | ) 35 | 36 | func (srv *Server) initialize(v interface{}) error { 37 | vTyp = reflect.TypeOf(v) 38 | vVal = reflect.ValueOf(v) 39 | err := srv.checkField() 40 | if err != nil { 41 | return err 42 | } 43 | err = srv.checkMethod() 44 | if err != nil { 45 | return err 46 | } 47 | fmt.Println("- Server has been initialized successfully.") 48 | return nil 49 | } 50 | 51 | func (srv *Server) launch() error { 52 | listener, err := net.ListenTCP("tcp", srv.LAddr) 53 | if err != nil { 54 | return errors.New("launch error: server listened failed.") 55 | } 56 | defer listener.Close() 57 | srv.Listener = listener 58 | fmt.Println("- Server has been launched successfully.") 59 | fmt.Println("- Server is listening..") 60 | for { 61 | conn, err := srv.Listener.Accept() 62 | if err != nil { 63 | err = errors.New("launch error: server accepted failed from " + conn.RemoteAddr().String()) 64 | return err 65 | } 66 | go srv.handle(conn) 67 | } 68 | return nil 69 | } 70 | 71 | func (srv *Server) handle(conn net.Conn) { 72 | defer conn.Close() 73 | taskChan := make(chan []byte, 10) 74 | go srv.process(taskChan, conn) 75 | dePacket(conn, &taskChan) 76 | return 77 | } 78 | 79 | func (srv *Server) process(taskChan chan []byte, conn net.Conn) { 80 | for recvBuffer := range taskChan { 81 | packetType := recvBuffer[0] 82 | data := recvBuffer[1:] 83 | tempReturn := mapNameFunc[originMap[packetType]].Call([]reflect.Value{vVal, reflect.ValueOf(data)}) 84 | ret := enPacket(packetType, tempReturn[0].Interface().([]byte)) 85 | bufferWriter := bufio.NewWriter(conn) 86 | bufferWriter.Write(ret) 87 | bufferWriter.Flush() 88 | } 89 | } 90 | 91 | func (srv *Server) checkField() error { 92 | Addr, err := commonFieldCheck() 93 | if err != nil { 94 | return err 95 | } 96 | srv.LAddr = Addr 97 | return nil 98 | } 99 | 100 | func commonFieldCheck() (*net.TCPAddr, error) { 101 | _, flag := vTyp.FieldByName(HOST_NAME) 102 | if !flag { 103 | err := errors.New("init error: field `" + HOST_NAME + "` not found.") 104 | return nil, err 105 | } 106 | fieldStruct, flag := vTyp.FieldByName(MAP_NAME) 107 | if !flag { 108 | err := errors.New("init error: field `" + MAP_NAME + "` not found.") 109 | return nil, err 110 | } 111 | mapType := fieldStruct.Type 112 | if mapType.Kind() != reflect.Map || mapType.String() != mapTypeString { 113 | err := errors.New("init error: the type of `Pmap` field is supposed to be " + mapTypeString + ".") 114 | return nil, err 115 | } 116 | hostVal := vVal.FieldByName(HOST_NAME).Interface() 117 | host := string(hostVal.(string)) 118 | rawHost := strings.TrimSpace(host) 119 | if len(rawHost) < 1 { 120 | err := errors.New("init error: value of `" + HOST_NAME + "` has not been assigned.") 121 | return nil, err 122 | } 123 | Laddr, err := net.ResolveTCPAddr("tcp", rawHost) 124 | if err != nil { 125 | err = errors.New("init error: " + err.Error() + ".") 126 | return nil, err 127 | } 128 | mapVal := vVal.FieldByName(MAP_NAME).Interface() 129 | originMap = map[byte]string(mapVal.(map[byte]string)) 130 | moduleNum = len(originMap) 131 | for index, value := range originMap { 132 | if moduleNames[value] != nil { 133 | err := errors.New("init error: values of `Pmap` repeated.") 134 | return nil, err 135 | } 136 | moduleNames[value] = &index 137 | } 138 | return Laddr, nil 139 | } 140 | 141 | func (srv *Server) checkMethod() error { 142 | err := commonMethodCheck() 143 | if err != nil { 144 | return err 145 | } 146 | for i := 0; i < moduleNum; i++ { 147 | tempMethod := vTyp.Method(i) 148 | if moduleNames[tempMethod.Name[1:]] == nil { 149 | err := errors.New("init error: methods are defined incorrectly.") 150 | return err 151 | } 152 | if tempMethod.Type.In(1) != byteSliceType || tempMethod.Type.Out(0) != byteSliceType { 153 | err := errors.New("init error: method `" + tempMethod.Name + "` has wrong type of params or values.") 154 | return err 155 | } 156 | mapNameFunc[tempMethod.Name[1:]] = tempMethod.Func 157 | } 158 | return nil 159 | } 160 | 161 | func commonMethodCheck() error { 162 | methodNum := vTyp.NumMethod() 163 | if methodNum != moduleNum { 164 | err := errors.New("init error: methods are not defined completely.") 165 | return err 166 | } 167 | return nil 168 | } 169 | 170 | func New(v interface{}) error { 171 | server := &Server{} 172 | err := server.initialize(v) 173 | if err != nil { 174 | return err 175 | } 176 | err = server.launch() 177 | if err != nil { 178 | return err 179 | } 180 | return nil 181 | } 182 | --------------------------------------------------------------------------------