标签:reader main 而不是 inpu tcp协议 新建 fail reads 快速
使用的协议是tcp,由于tcp协议传输数据的时候会有粘包现象,所以为了解决消除这个现象,又编写了两个工具函数Encode和Decode
消除粘包现象的方法是在自定义一个应用层协议,他的内容为每次发送的数据包的前4个字节表示数据的长度,然后后面才是真正发送的数据
首先是工具包:
proto.go
package proto import ( "bytes" "encoding/binary" "fmt" "net" ) // 每次发送的数据包前4个字节用来记录数据的长度,后面才是记录数据,这样一方面可以准确获知应该读取的长度,并且可以防止粘包现象的发生 // 粘包现象 // 在tcp这种面相连接的字节流协议中,例如客户端发送数据给服务端,客户端在快速向发送缓冲区中写入数据时,就有可能发生粘包现象 // 例如首先客户端在向发送缓冲区中写入数据abc,然后紧接着又写入def,那么为了保证发送数据的效率,发送缓冲区中的数据并不会马上发送出去,而是会等待一段时间,如果这段时间 // 还有其他数据到达了发送缓冲区,那么发送缓冲区会将这一段时间中到达的数据都发送出去,而不是将每条数据单独发送 // 这就是粘包现象 // Encode ... func Encode(msg string) ([]byte, error) { length := int32(len(msg)) // 新建一个缓冲区,这里是获取这个缓冲区的指针 pkg := new(bytes.Buffer) err := binary.Write(pkg, binary.BigEndian, length) // 这里一定要将msg转化为[]byte的切片 err = binary.Write(pkg, binary.BigEndian, []byte(msg)) if err != nil { fmt.Println("write msg fail,err:", err) return nil, err } return pkg.Bytes(), nil } // Decode ... func Decode(conn net.Conn) (int32, string, error) { lengthSlice := make([]byte, 4) _, err := conn.Read(lengthSlice) if err != nil { fmt.Println("Get msg‘s length fail,err:", err) return 0, "", err } // 将切片转化为一个缓冲区,切片中的内容就是缓冲区中的内容,切片必须是[]byte的切片 lengthBuffer := bytes.NewBuffer(lengthSlice) var length int32 err = binary.Read(lengthBuffer, binary.BigEndian, &length) if err != nil { fmt.Println("Parse the lenth to int32 fail,err:", err) return 0, "", err } msgSlice := make([]byte, length) _, err = conn.Read(msgSlice) if err != nil { fmt.Println("Get the msg fail,err:", err) return 0, "", err } msgBuffer := bytes.NewBuffer(msgSlice) var msg string // 将缓冲区中的数据按照大端的方式重新放入切片中 err = binary.Read(msgBuffer, binary.BigEndian, msgSlice) msg = string(msgSlice) if err != nil { fmt.Println("Parse the msg to string fail,err:", err) return 0, "", err } return length, msg, nil }
然后是server端
package main import ( "fmt" "learnGO/socket/proto" "net" ) func main() { listener, err := net.Listen("tcp", "127.0.0.1:4396") if err != nil { fmt.Println("Listen the ip and port err:", err) return } defer listener.Close() // 循环建立连接 for { conn, err := listener.Accept() if err != nil { fmt.Println("Establish the connnect failed,err:", err) return } go process(conn) } } func process(conn net.Conn) { defer conn.Close() for { // recv := make([]byte, 1024) // n, err := conn.Read(recv) // if err == io.EOF { // continue // } // if err != nil { // fmt.Println("Read msg from read buffer fail,err:", err) // return // } // fmt.Printf("%d:%s", n, string(recv)) length, msg, err := proto.Decode(conn) if err != nil { return } fmt.Printf("%d:%s", length, msg) } }
然后是client端
package main import ( "bufio" "fmt" "learnGO/socket/proto" "net" "os" ) func main() { // 向ip和端口拨号进行连接 conn, err := net.Dial("tcp", "127.0.0.1:4396") if err != nil { fmt.Println("Connect to the server fail,err:", err) return } reader := bufio.NewReader(os.Stdin) for { fmt.Println("plz input:") msg, err := reader.ReadString(‘\n‘) if err != nil { fmt.Println("Input msg fail,err:", err) return } b, err := proto.Encode(msg) if err != nil { return } n, err := conn.Write(b) if err != nil { fmt.Println("Send msg fail,err:", err) return } fmt.Println(n) } }
标签:reader main 而不是 inpu tcp协议 新建 fail reads 快速
原文地址:https://www.cnblogs.com/gyyyl/p/13024497.html