网络通信之拆包粘包处理(二)

上文介绍了拆包粘包的概念和按照固定分隔符的例子。

本文继续介绍消息头和包体的方案。

废话不多少直接上代码:

server:

// server.go
package main

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

const headerSize = 4 // Assuming a 4-byte header for simplicity

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

	reader := bufio.NewReader(conn)

	for {
		// Read the fixed-length header
		headerBytes := make([]byte, headerSize)
		_, err := reader.Read(headerBytes)
		if err != nil {
			fmt.Println("Error reading header:", err)
			return
		}

		// Convert the header to an integer to get the message length
		messageLength := int(binary.BigEndian.Uint32(headerBytes))

		// Read the message content based on the length
		messageBytes := make([]byte, messageLength)
		_, err = reader.Read(messageBytes)
		if err != nil {
			fmt.Println("Error reading message content:", err)
			return
		}

		message := string(messageBytes)
		fmt.Printf("Received message: %s\n", message)
	}
}

func main() {
	listener, err := net.Listen("tcp", "127.0.0.1:12345")
	if err != nil {
		fmt.Println("Error listening:", err)
		return
	}
	defer listener.Close()

	fmt.Println("Server is listening on port 12345")

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Error accepting connection:", err)
			continue
		}
		go handleConnection(conn)
	}
}

client:

// client.go
package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"net"
	"os"
)

const headerSize = 4 // Assuming a 4-byte header for simplicity

func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:12345")
	if err != nil {
		fmt.Println("Error connecting:", err)
		return
	}
	defer conn.Close()

	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("Enter message (or 'exit' to quit): ")
		message, _ := reader.ReadString('\n')
		if message == "exit\n" {
			break
		}

		// Convert the length to a 4-byte header
		length := len(message)
		header := make([]byte, headerSize)
		binary.BigEndian.PutUint32(header, uint32(length))

		// Send the length-prefixed message to the server
		conn.Write(append(header, []byte(message)...))
	}
}

在这个例子中,服务器和客户端各定义了一个长度4字节的消息头。消息头存储消息的长度,服务器读取消息头以确定传入消息的长度。这种方法允许根据特定应用程序的需要在标头中包含其他信息。根据的需求调整消息头大小。

运行结果:

固定包长

顾名思义就是每次发送消息为固定的长度,接收完毕后也按照该长度进行分割。

代码如下:

// 服务器端代码
package main

import (
	"fmt"
	"net"
)

const (
	packetLength = 4
)

func main() {
	listener, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		return
	}
	defer listener.Close()

	fmt.Println("Server started. Listening on localhost:8080")

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Error accepting connection:", err.Error())
			return
		}

		go handleConnection(conn)
	}
}

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

	fmt.Println("New client connected:", conn.RemoteAddr())

	buffer := make([]byte, 0)

	for {
		data := make([]byte, 1024)
		n, err := conn.Read(data)
		if err != nil {
			fmt.Println("Error reading data:", err.Error())
			return
		}

		buffer = append(buffer, data[:n]...)

		for len(buffer) >= packetLength {
			packet := buffer[:packetLength]
			buffer = buffer[packetLength:]

			processPacket(packet)
		}
	}
}

func processPacket(packet []byte) {
	fmt.Println("Received packet:", packet)
}

// 客户端代码
package main

import (
	"fmt"
	"net"
	"time"
)

const (
	packetLength = 4
)

func main() {
	conn, err := net.Dial("tcp", "localhost:8080")
	if err != nil {
		fmt.Println("Error connecting:", err.Error())
		return
	}
	defer conn.Close()

	fmt.Println("Connected to server.")

	packet := []byte{0x01, 0x02}
	sendPacket(conn, packet)

	time.Sleep(2 * time.Second)

	packet = []byte{0x05, 0x06, 0x07, 0x08}
	sendPacket(conn, packet)
}

func sendPacket(conn net.Conn, packet []byte) {
	// 如果消息长度不够4字节,补充0x00字节
	if len(packet) < packetLength {
		padding := make([]byte, packetLength-len(packet))
		packet = append(packet, padding...)
	}

	_, err := conn.Write(packet)
	if err != nil {
		fmt.Println("Error sending packet:", err.Error())
		return
	}

	fmt.Println("Sent packet:", packet)
}

如果客户端发送单条消息长度不够固定长度,则使用0x00补全,就可以保证服务器每次能正常的解析到整包。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值