Go语言网络编程

Posted gyyyl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言网络编程相关的知识,希望对你有一定的参考价值。

使用的协议是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(‘
‘)
		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)
	}
}

  

以上是关于Go语言网络编程的主要内容,如果未能解决你的问题,请参考以下文章

一天一门编程语言设计一套Go语言中的 Stream API 接口代码实现

编程模式之Go语言如何实现装饰器

[Go] 使用go语言解决现代编程难题

golang代码片段(摘抄)

编程实践Go 语言手册《Go极简教程》

c# https请求忽略证书验证_各种编程语言忽略http的SSL证书认证