因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。
关于粘包和拆包可以参考下图的几种情况:
发送端将每个包都封装成固定的长度,比如20字节大小。如果不足20字节可通过补0或空等进行填充到指定长度;
package main
import (
"fmt"
"log"
"net"
)
func main() {
// 监听指定的TCP端口
listener, err := net.Listen("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("Server started. Listening on localhost:8080...")
// 接收客户端连接
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
// 启动一个并发的goroutine来处理连接
go handleConnection(conn)
}
}
// 处理连接
func handleConnection(conn net.Conn) {
defer conn.Close()
// 读取固定长度的数据
fixedLength := 20 // 假设要读取的数据固定长度为20字节
buffer := make([]byte, fixedLength)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Received data: %s\n", string(buffer))
// 可以在这里对接收到的数据进行处理和响应
// ...
// 发送响应给客户端
response := "Hello, Client!"
_, err = conn.Write([]byte(response))
if err != nil {
log.Fatal(err)
}
fmt.Println("Response sent successfully!")
}
package main
import (
"fmt"
"log"
"net"
)
func main() {
// 建立TCP连接
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// 发送固定长度的数据
message := "Hello, Server!"
fixedLength := 20 // 假设要发送的数据固定长度为20字节
// 如果消息长度小于固定长度,则使用空字符填充
if len(message) < fixedLength {
padding := make([]byte, fixedLength-len(message))
message += string(padding)
}
_, err = conn.Write([]byte(message))
if err != nil {
log.Fatal(err)
}
fmt.Println("Data sent successfully!")
}
发送端在每个包的末尾使用固定的分隔符,例如\n
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
)
func main() {
// 监听指定的TCP端口
listener, err := net.Listen("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("Server started. Listening on localhost:8080...")
// 接收客户端连接
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
// 启动一个并发的goroutine来处理连接
go handleConnection(conn)
}
}
// 处理连接
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
// 读取一行数据,以分隔符"\n"作为结束标志
message, err := reader.ReadString('\n')
if err != nil {
log.Println(err)
break
}
// 去除消息中的换行符
message = strings.TrimRight(message, "\n")
fmt.Printf("Received message: %s\n", message)
// 可以在这里对接收到的消息进行处理和响应
// ...
// 发送响应给客户端
response := "Hello, Client!\n"
_, err = conn.Write([]byte(response))
if err != nil {
log.Println(err)
break
}
}
fmt.Println("Connection closed.")
}
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
func main() {
// 建立TCP连接
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
reader := bufio.NewReader(os.Stdin)
for {
// 读取用户输入的消息
fmt.Print("Enter message: ")
message, err := reader.ReadString('\n')
if err != nil {
log.Println(err)
break
}
// 发送消息给服务器
_, err = conn.Write([]byte(message))
if err != nil {
log.Println(err)
break
}
// 读取服务器的响应
response, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Println(err)
break
}
fmt.Printf("Server response: %s", response)
}
fmt.Println("Connection closed.")
}
将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
)
const headerSize = 4 // 头部长度的字节数
func main() {
// 启动服务器
go startServer()
// 连接到服务器
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("连接服务器失败:", err)
return
}
defer conn.Close()
// 发送消息
message := "Hello, Server!"
sendMessage(conn, message)
// 读取服务器响应
response, err := readMessage(conn)
if err != nil {
fmt.Println("读取消息失败:", err)
return
}
fmt.Println("服务器响应:", response)
}
func startServer() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("启动服务器失败:", err)
return
}
defer listener.Close()
fmt.Println("服务器已启动,等待连接...")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("接受连接失败:", err)
return
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
fmt.Printf("客户端 %s 已连接\n", conn.RemoteAddr().String())
defer conn.Close()
// 读取消息
message, err := readMessage(conn)
if err != nil {
fmt.Println("读取消息失败:", err)
return
}
fmt.Println("收到消息:", message)
// 发送响应
response := "Hello, Client!"
sendMessage(conn, response)
}
func sendMessage(conn net.Conn, message string) error {
// 计算消息长度
messageLength := len(message)
// 将消息长度写入头部
header := make([]byte, headerSize)
binary.BigEndian.PutUint32(header, uint32(messageLength))
if _, err := conn.Write(header); err != nil {
return fmt.Errorf("写入消息头部失败: %v", err)
}
// 写入消息体
if _, err := conn.Write([]byte(message)); err != nil {
return fmt.Errorf("写入消息体失败: %v", err)
}
return nil
}
func readMessage(conn net.Conn) (string, error) {
// 读取消息头部
header := make([]byte, headerSize)
if _, err := io.ReadFull(conn, header); err != nil {
return "", fmt.Errorf("读取消息头部失败: %v", err)
}
// 解析消息长度
messageLength := binary.BigEndian.Uint32(header)
// 读取消息体
message := make([]byte, messageLength)
if _, err := io.ReadFull(conn, message); err != nil {
return "", fmt.Errorf("读取消息体失败: %v", err)
}
return string(message), nil
}