El paquete binario de Go y su aplicación.

Orden de bytes de computadora y orden de bytes de red
El orden de bytes es el orden de almacenamiento de tipos de datos de varios bytes (int, float, etc.) en la memoria. Se puede dividir en orden big endian, el extremo de la dirección baja almacena el byte alto, el orden little endian es el opuesto y el extremo de la dirección baja almacena el byte de orden bajo.

Dentro de la computadora, little endian se usa ampliamente para almacenar datos dentro de las CPU modernas, mientras que big endian se usa en otros escenarios, como la transmisión de red y el almacenamiento de archivos.

Al usar little endian, puede cambiar el tamaño de la memoria ocupada por número sin mover los bytes sin el bit de inicio de la dirección de memoria. Por ejemplo, si quiero convertir un entero int32 de cuatro bytes en un entero int64 de ocho bytes, solo necesito agregar ceros al final de la secuencia little endian. La operación anterior de expandir o reducir variables enteras es muy útil a nivel de compilador, pero no a nivel de protocolo de red.

Cuando se operan números binarios en la capa de protocolo de red, se acuerda utilizar el orden big-endian, que es el método utilizado para la transmisión de bytes de red. Debido a que el byte más significativo de la secuencia big-endian es el primero (el extremo de la dirección inferior almacena el byte de orden superior), se puede ordenar de acuerdo con el diccionario, por lo que podemos comparar cada byte del número codificado en binario.

ByteOrder
ByteOrder especifica cómo convertir una secuencia de bytes en un número entero sin signo de 16, 32 o 64 bits:

type ByteOrder interface {
    
    
	Uint16([]byte) uint16
	Uint32([]byte) uint32
	Uint64([]byte) uint64
	PutUint16([]byte, uint16)
	PutUint32([]byte, uint32)
	PutUint64([]byte, uint64)
	String() string
}

pequeñoEndian:

littleEndian no se puede crear en otros paquetes, pero se ha creado una estructura llamada LittleEndian en binario, que podemos usar directamente.

var LittleEndian littleEndian

type littleEndian struct{
    
    }

func (littleEndian) Uint16(b []byte) uint16 {
    
    
	_ = b[1] // 编译器的边界检测提示
	return uint16(b[0]) | uint16(b[1])<<8
}

func (littleEndian) PutUint16(b []byte, v uint16) {
    
    
	_ = b[1] // early bounds check to guarantee safety of writes below
	b[0] = byte(v)
	b[1] = byte(v >> 8)
}

func (littleEndian) Uint32(b []byte) uint32 {
    
    
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}

func (littleEndian) PutUint32(b []byte, v uint32) {
    
    
	_ = b[3] // early bounds check to guarantee safety of writes below
	b[0] = byte(v)
	b[1] = byte(v >> 8)
	b[2] = byte(v >> 16)
	b[3] = byte(v >> 24)
}

func (littleEndian) Uint64(b []byte) uint64 {
    
    
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}

func (littleEndian) PutUint64(b []byte, v uint64) {
    
    
	_ = b[7] // early bounds check to guarantee safety of writes below
	b[0] = byte(v)
	b[1] = byte(v >> 8)
	b[2] = byte(v >> 16)
	b[3] = byte(v >> 24)
	b[4] = byte(v >> 32)
	b[5] = byte(v >> 40)
	b[6] = byte(v >> 48)
	b[7] = byte(v >> 56)
}

func (littleEndian) String() string {
    
     return "LittleEndian" }

func (littleEndian) GoString() string {
    
     return "binary.LittleEndian" }

El método definido anteriormente también es relativamente simple, que es la conversión entre secuencias de bytes y números sin signo. Por ejemplo, el método de Uint16 es little-endian aquí, por lo que el byte bajo se almacena en el espacio de direcciones bajo. A medida que aumenta el índice del segmento, el espacio de direcciones también aumenta, por lo que el espacio donde se encuentra b[1] es dirección alta, así que cambie b[1] a la izquierda ocho bits y luego combínelo con b[0] para obtener datos uint16.

bigendian:

大端与小端相反:
var BigEndian bigEndian

type bigEndian struct{
    
    }

func (bigEndian) Uint16(b []byte) uint16 {
    
    
	_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
	return uint16(b[1]) | uint16(b[0])<<8
}

func (bigEndian) PutUint16(b []byte, v uint16) {
    
    
	_ = b[1] // early bounds check to guarantee safety of writes below
	b[0] = byte(v >> 8)
	b[1] = byte(v)
}

func (bigEndian) Uint32(b []byte) uint32 {
    
    
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}

func (bigEndian) PutUint32(b []byte, v uint32) {
    
    
	_ = b[3] // early bounds check to guarantee safety of writes below
	b[0] = byte(v >> 24)
	b[1] = byte(v >> 16)
	b[2] = byte(v >> 8)
	b[3] = byte(v)
}

func (bigEndian) Uint64(b []byte) uint64 {
    
    
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

func (bigEndian) PutUint64(b []byte, v uint64) {
    
    
	_ = b[7] // early bounds check to guarantee safety of writes below
	b[0] = byte(v >> 56)
	b[1] = byte(v >> 48)
	b[2] = byte(v >> 40)
	b[3] = byte(v >> 32)
	b[4] = byte(v >> 24)
	b[5] = byte(v >> 16)
	b[6] = byte(v >> 8)
	b[7] = byte(v)
}

func (bigEndian) String() string {
    
     return "BigEndian" }

func (bigEndian) GoString() string {
    
     return "binary.BigEndian" }

Cuando usamos tcp para transmitir datos, a menudo nos encontramos con el fenómeno de los paquetes adhesivos, por lo que para resolver los paquetes adhesivos, debemos decirle a la otra parte el tamaño de los paquetes de datos que enviamos. Generalmente, se utiliza un protocolo de datos de tipo TLV, a saber, Type, Len, Value, Type y Len son encabezados de datos, y estos dos campos se pueden fijar en cuatro bytes. Al leer datos, primero lea el tipo y la longitud, y luego lea los datos restantes de acuerdo con la longitud:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
)

// 对数据进行编码
func Encode(id uint32, msg []byte) []byte {
    
    
	var dataLen uint32 = uint32(len(msg))

	// *Buffer实现了Writer
	buffer := bytes.NewBuffer([]byte{
    
    })
    // 将id写入字节切片
	if err := binary.Write(buffer, binary.LittleEndian, &id); err != nil {
    
    
		fmt.Println("Write to buffer error:", err)
	}
	// 将数据长度写入字节切片
	if err := binary.Write(buffer, binary.LittleEndian, &dataLen); err != nil {
    
    
		fmt.Println("Write to buffer error:", err)
	}
	
    // 最后将数据添加到后面
	msg = append(buffer.Bytes(), msg...)

	return msg
}

func main() {
    
    
	dial, err := net.Dial("tcp4", "127.0.0.1:6666")
	if err != nil {
    
    
		fmt.Println("Dial tcp error:", err)
	}
	
    // 向服务端发送hello,world!
	msg := []byte("hello,world!")
	var id uint32 = 1

	data := Encode(id, msg)
	dial.Write(data)

	dial.Close()
}

servidor:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
)

// 解码,从字节切片中获取id和len
func Decode(encoded []byte) (id uint32, l uint32) {
    
    
	buffer := bytes.NewBuffer(encoded)
	if err := binary.Read(buffer, binary.LittleEndian, &id); err != nil {
    
    
		fmt.Println("Read from buffer error:", err)
	}

	if err := binary.Read(buffer, binary.LittleEndian, &l); err != nil {
    
    
		fmt.Println("Read from buffer error:", err)
	}

	return id, l
}

const MAX_PACKAGE = 4096

func DealConn(conn net.Conn) {
    
    
	defer conn.Close()

	head := make([]byte, 8)
	for {
    
    
        // 先读取8个字节的头部,也就是id和dataLen
		_, err := io.ReadFull(conn, head)
		if err != nil {
    
    
			if err == io.EOF {
    
    
				fmt.Println("Connection has been closed by client")
			} else {
    
    
				fmt.Println("Read error:", err)
			}
			return
		}

		id, l := Decode(head)
		if l > MAX_PACKAGE {
    
    
			fmt.Println("Received data grater than MAX_PACKAGE")
			return
		}
		
        // 然后读取剩余数据
		data := make([]byte, l)
		_, err = io.ReadFull(conn, data)
		if err != nil {
    
    
			if err == io.EOF {
    
    
				fmt.Println("Connection has been closed by client")
			} else {
    
    
				fmt.Println("Read error:", err)
			}
			return
		}

        // 打印收到的数据
		fmt.Printf("Receive Data, Type:%d, Len:%d, Message:%s\n",
			id, l, string(data))
	}

}

func main() {
    
    

	listener, err := net.Listen("tcp", "127.0.0.1:6666")
	if err != nil {
    
    
		fmt.Println("Listen tcp error:", err)
		return
	}


	for {
    
    
		conn, err := listener.Accept()
		if err != nil {
    
    
			fmt.Println("Accept error:", err)
            break
		}

		// 启动一个协程处理客户端
		go DealConn(conn)

	}

}

Supongo que te gusta

Origin blog.csdn.net/qq_30505673/article/details/130889239
Recomendado
Clasificación