美文网首页工作生活
从零开始的Go Socket Server(二):支持结构化数据

从零开始的Go Socket Server(二):支持结构化数据

作者: 洞渊的自习室 | 来源:发表于2019-07-03 09:38 被阅读0次

    连续发送数据的问题

    首先从Client端开始修改,让Client连续的发送多个json数据,看下上一节的Server端的输出有什么问题。
    修改send方法,将发送的数据替换为连续的json,每个json数据中包含一个index和时间戳

    func send(conn net.Conn) {
        for i := 0; i < 30; i++ {
            dic := make(map[string]interface{})
            dic["index"] = i
            dic["timestamp"] = time.Now().Format(time.RFC3339)
            jsonString, err := json.Marshal(dic)
            if err != nil {
                log.Println(err)
            }
            _, err = conn.Write([]byte(jsonString))
            if err != nil {
                log.Println(err)
            }
        }
        log.Println("send finished")
    }
    

    输出的数据会出现这种情况,多个json数据包连接在了一起。

    127.0.0.1:53709 receive data string:
     {"index":0,"timestamp":"2019-07-02T11:29:32+08:00"}{"index":1,"timestamp":"2019-07-02T11:29:32+08:00"}{"index":2,"timestamp":"2019-07-02T11:29:32+08:00"}
    

    很明显,这样的数据格式是不能直接解析的。所以我们需要一个数据传输协议来提取正确的数据格式。
    这个地方我们使用一个标志字符串(Header)和数据长度(长度数据本身占用5个字符串长度)来作为传输数据解析的依据。
    在Client端首先计算要发送的数据长度,然后将长度写在发送数据的前面。Server端接收到数据后读取长度信息,然后截取指定的长度数据,如果一次报文长度不够则等待下次的报文一起进行解析。

    增加传输协议

    首先我们定义数据长度的最大字符数是5位,即最大99999个字符。然后在发送数据时将Header和字符长度写到发送数据的前面。

    func send(conn net.Conn) {
        for i := 0; i < 30; i++ {
            dic := make(map[string]interface{})
            dic["index"] = i
            dic["timestamp"] = time.Now().Format(time.RFC3339)
            jsonString, err := json.Marshal(dic)
            if err != nil {
                log.Println(err)
            }
            length := len(jsonString)
            if length > 99999 {
                //Header中标识字符串长度的最大为99999
                panic("data is too long to send")
            }
            lengthText := strconv.Itoa(length)
            textLength := fmt.Sprintf("%05s", lengthText)[:5]
            headerText := append([]byte("Header"), textLength...)
            jsonString = append(headerText, jsonString...)
            _, err = conn.Write([]byte(jsonString))
            if err != nil {
                log.Println(err)
            }
            log.Println("send : ", jsonString)
        }
        log.Println("send finished")
    }
    

    每次发送的数据变成了这个样子

    Header00052{"index":28,"timestamp":"2019-07-03T09:27:04+08:00"}
    

    在服务端对接收到的数据进行拆解,

    const HeaderText = "Header"
    const HeaderTextLength = len(HeaderText)
    const LengthTextLength = 5
    
    func handleConnection(conn net.Conn) {
        buffer := make([]byte, 2048, 2048)
        for {
            n, err := conn.Read(buffer)
            if err != nil {
                log.Println(conn.RemoteAddr().String(), " read error: ", err)
                return
            }
            tmpBuffer := buffer[:n]
            log.Println(conn.RemoteAddr().String(), "receive data string:")
            //解析buffer中的内容
            for {
                if len(tmpBuffer) == 0 {
                    break
                }
    
                if string(tmpBuffer[:HeaderTextLength]) != HeaderText {
                    panic("buffer not started with 'Header'")
                }
                lengthText := string(tmpBuffer[HeaderTextLength: HeaderTextLength + LengthTextLength])
                textLength, err := strconv.Atoi(lengthText)
                if err != nil {
                    log.Println(err)
                }
                content := tmpBuffer[HeaderTextLength + LengthTextLength: HeaderTextLength + LengthTextLength + textLength]
                tmpBuffer = tmpBuffer[HeaderTextLength + LengthTextLength + textLength:]
    
                fmt.Println(string(content))
            }
    
        }
    }
    

    每次读到数据后,先读取接下来的数据的长度信息,然后根据长度截取数据,剩下的数据继续按照这个流程进行处理。最终将数据拆分成单条的json数据。
    现在是一个Client在向Server发送数据,实际情况会有大量的Client,所以下一步我们要增加对多个Client的支持。
    完整的代码在goSocket,Tag 1.1

    相关文章

      网友评论

        本文标题:从零开始的Go Socket Server(二):支持结构化数据

        本文链接:https://www.haomeiwen.com/subject/xqxzcctx.html