GO学习笔记(9)
九. 网络编程 , 直接上代码
9.1 tcp协议
9.1.1. 基本使用
// server端代码
package main import ( "bufio" "fmt" "io" "net" ) func main() { listen, err := net.Listen("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("监听端口失败:", err) return } fmt.Println("tcp服务已经启动") for { conn, err := listen.Accept() if err != nil { fmt.Println("连接失败:", err) continue } go process1(conn) // 启动一个goroutine处理连接 } } // 简单使用 func process1(conn net.Conn) { defer conn.Close() // 关闭连接 for { reader := bufio.NewReader(conn) var buf [128]byte n, err := reader.Read(buf[:]) // 读取数据 if err != nil { fmt.Println("读取数据失败:", err) break } recvStr := string(buf[:n]) fmt.Println("收到客户端发来的数据:", recvStr) conn.Write([]byte("服务器说: " + recvStr)) // 发送数据 } }
// client端代码
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { demo1() } // 简单使用 func demo1() { conn, err := net.Dial("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("连接失败 :", err) return } defer conn.Close() // 关闭连接 inputReader := bufio.NewReader(os.Stdin) for { input, _ := inputReader.ReadString('\n') // 读取用户输入 inputInfo := strings.Trim(input, "\r\n") if strings.ToUpper(inputInfo) == "Q" { // 如果输入q就退出 return } _, err = conn.Write([]byte(inputInfo)) // 发送数据 if err != nil { return } buf := [512]byte{} n, err := conn.Read(buf[:]) // 接受数据 if err != nil { fmt.Println("接收服务器数据失败:", err) return } fmt.Println("接收服务器数据: ", string(buf[:n])) } }
9.1.2. 粘包问题
// server端代码
package main import ( "bufio" "fmt" "io" "net" ) func main() { listen, err := net.Listen("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("监听端口失败:", err) return } fmt.Println("tcp服务已经启动") for { conn, err := listen.Accept() if err != nil { fmt.Println("连接失败:", err) continue } go process2(conn) // 启动一个goroutine处理连接 } } // 粘包 func process2(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) var buf [1024]byte for { n, err := reader.Read(buf[:]) if err == io.EOF { break } if err != nil { fmt.Println("read from client failed, err:", err) break } recvStr := string(buf[:n]) fmt.Println("收到client发来的数据:", recvStr) // 收到client发来的数据: 完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了 // 收到client发来的数据: 完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了完蛋了,粘包了 } }
// client端代码
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { demo2() } // 粘包产生的原因: // 1.由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。 // 2.接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。 func demo2() { conn, err := net.Dial("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("连接失败 :", err) return } defer conn.Close() // 关闭连接 msg := `完蛋了,粘包了` for i := 0; i < 20; i++ { conn.Write([]byte(msg)) } }
9.1.3. 解决粘包问题: 封包
// 封包解包代码
package proto import ( "bufio" "bytes" "encoding/binary" ) // Encode 将消息编码 func Encode(message string) ([]byte, error) { // 读取消息的长度,转换成int32类型(占4个字节) var length = int32(len(message)) var pkg = new(bytes.Buffer) // 写入消息头 err := binary.Write(pkg, binary.LittleEndian, length) if err != nil { return nil, err } // 写入消息实体 err = binary.Write(pkg, binary.LittleEndian, []byte(message)) if err != nil { return nil, err } return pkg.Bytes(), nil } // Decode 解码消息 func Decode(reader *bufio.Reader) (string, error) { // 读取消息的长度 lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据 lengthBuff := bytes.NewBuffer(lengthByte) var length int32 err := binary.Read(lengthBuff, binary.LittleEndian, &length) if err != nil { return "", err } // Buffered返回缓冲中现有的可读取的字节数。 if int32(reader.Buffered()) < length+4 { return "", err } // 读取真正的消息数据 pack := make([]byte, int(4+length)) _, err = reader.Read(pack) if err != nil { return "", err } return string(pack[4:]), nil }
// sever端代码
package main import ( "bufio" "fmt" "github.com/icanyou/demo/socket/proto" "io" "net" ) func main() { listen, err := net.Listen("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("监听端口失败:", err) return } fmt.Println("tcp服务已经启动") for { conn, err := listen.Accept() if err != nil { fmt.Println("连接失败:", err) continue } go process3(conn) // 启动一个goroutine处理连接 } }// 解决粘包 func process3(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) for { msg, err := proto.Decode(reader) if err == io.EOF { return } if err != nil { fmt.Println("解码失败:", err) return } fmt.Println("收到client发来的数据:", msg) } }
// client端代码
package main import ( "bufio" "fmt" "github.com/icanyou/demo/socket/proto" "net" "os" "strings" ) func main() { demo3() }// 处理粘包 func demo3() { conn, err := net.Dial("tcp", "127.0.0.1:50001") if err != nil { fmt.Println("dial failed, err", err) return } defer conn.Close() for i := 0; i < 20; i++ { msg := `会不会粘包?` data, err := proto.Encode(msg) if err != nil { fmt.Println("encode msg failed, err:", err) return } conn.Write(data) } }
9.2. http
// server端代码
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", handle) http.ListenAndServe("127.0.0.1:8000", nil) } // handler函数 func handle(w http.ResponseWriter, r *http.Request) { fmt.Println(r.RemoteAddr, "连接成功") fmt.Println("method:", r.Method) fmt.Println("url:", r.URL.Path) fmt.Println("header:", r.Header) fmt.Println("body:", r.Body) w.Write([]byte("hello world")) }
// client端代码
package main import ( "fmt" "io" "net/http" ) func main() { resp, _ := http.Get("http://127.0.0.1:8000/") defer resp.Body.Close() fmt.Println(resp.Status) fmt.Println(resp.Header) buf := make([]byte, 1024) for { // 接收服务端信息 n, err := resp.Body.Read(buf) if err != nil && err != io.EOF { fmt.Println(err) return } else { fmt.Println("读取完毕") res := string(buf[:n]) fmt.Println(res) break } } }
9.3. websocket