Go语言网络编程
2020-12-29 07:29
标签:reader main 而不是 inpu tcp协议 新建 fail reads 快速 使用的协议是tcp,由于tcp协议传输数据的时候会有粘包现象,所以为了解决消除这个现象,又编写了两个工具函数Encode和Decode 消除粘包现象的方法是在自定义一个应用层协议,他的内容为每次发送的数据包的前4个字节表示数据的长度,然后后面才是真正发送的数据 首先是工具包: proto.go 然后是server端 然后是client端 Go语言网络编程 标签:reader main 而不是 inpu tcp协议 新建 fail reads 快速 原文地址:https://www.cnblogs.com/gyyyl/p/13024497.htmlpackage 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
}
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)
}
}
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)
}
}
上一篇:【前端工程师手册】css会阻塞页面dom解析吗?javascript呢?
下一篇:当一个线程进入一个对象的 synchronized 方法 A 之后, 其它线程是否可进入此对象的 synchronized 方法 B?