Go解决TCP粘包问题

Server端main.go

package main

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

func HandleException(e error) {
	if e != nil {
		panic(e.Error())
	}
}

func main() {
	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	HandleException(err)
	listener, err := net.ListenTCP("tcp", addr)
	HandleException(err)
	fmt.Println("server listening on localhost:8080...")
	for {
		conn, err1 := listener.Accept()
		HandleException(err1)
		go func(connection net.Conn) {
			defer connection.Close()
			defer fmt.Println("==========connection closed=========")
			fmt.Println("==========connection on=========")
			//循环读取数据包
			for {
				//读超时时间
				errt := connection.SetReadDeadline(time.Now().Add(time.Second * 30))
				if errt != nil {
					fmt.Println("set time out failure")
					return
				}
				fmt.Println("================================")
				//先读4字节拿到内容长度
				lenBuf, err2 := readData(conn, 4)
				if err2 != nil {
					if err2 == io.EOF {
						fmt.Println("unable to receive the complete packet header, client connection closed...")
					} else {
						fmt.Println("something wrong happened, receive failure")
						fmt.Println(err2)
					}
					return
				}
				//网络字节序转主机字节序
				conLen := binary.BigEndian.Uint32(lenBuf)
				if conLen == 0 {
					fmt.Println("INFO: shutting down...")
					break
				}
				fmt.Printf("INFO: preparing to receive %d Bytes data...\n", conLen)

				//读取并打印数据
				dataBuf, err2 := readData(conn, conLen)
				if err2 != nil {
					if err2 == io.EOF {
						fmt.Printf("INFO: client connection closed, unable to load the complete data, received %d Bytes\n", len(dataBuf))
						fmt.Println(string(dataBuf))
						break
					} else {
						fmt.Println("INFO: something wrong happened, receive failure")
						fmt.Println(err2)
						return
					}
				}
				fmt.Printf("INFO: execpted %d Bytes, actual %d Bytes\n", conLen, len(dataBuf))
				fmt.Printf("DATA:\n%s\n", string(dataBuf))
			}
			//响应
			sendData(conn, []byte("all data loaded"))

		}(conn)
	}

}

// 发送数据函数
func sendData(conn net.Conn, buf []byte) (n int) {
	conLen := make([]byte, 4)
	binary.BigEndian.PutUint32(conLen, uint32(len(buf)))
	data := append(conLen, buf...)
	n, _ = conn.Write(data)
	return
}

func readData(conn net.Conn, size uint32) ([]byte, error) {
	left := int(size)
	var data []byte
	for left > 0 {
		tmp := make([]byte, left)
		if n, err := conn.Read(tmp); err == nil {
			left -= n
			data = append(data, tmp...)
		} else {
			if err == io.EOF {
				data = append(data, tmp...)
				return data, err
			} else {
				return nil, err
			}
		}
	}
	return data, nil
}

Client端main.go

package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
)

func HandleException(e error) {
	if e != nil {
		panic(e.Error())
	}
}

func main() {
	//setup
	serverAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	HandleException(err)
	conn, err := net.DialTCP("tcp", nil, serverAddr)
	HandleException(err)
	defer conn.Close()
	//write

	//读取文件并发送,一次发送随机长度,最大1024B
	file, err := os.OpenFile("./material.txt", os.O_RDONLY, 0666)
	HandleException(err)
	defer file.Close()
	fileReader := bufio.NewReader(file)

	//循环读文件并发送
	for {
		//随机读取长度
		size := rand.Intn(1024)
		//缓冲区
		buf := make([]byte, size)

		//读取
		if n, err1 := fileReader.Read(buf); err1 == nil {
			//发送数据
			n = sendData(conn, buf[:n])
			fmt.Printf("client: sent %d Bytes data...\n", n-4)
		} else {
			if err1 == io.EOF {
				//发送
				n = sendData(conn, buf[:n])
				fmt.Printf("client: sent %d Bytes data...\n", n-4)
				break
			} else {
				fmt.Printf("INFO: something wrong happened:\n%+v", err1)
				return
			}
		}
	}

	//接收响应
	fmt.Println("INFO: waiting for response....")
	//先读4字节拿到内容长度
	lenBuf, err := readData(conn, 4)
	//网络字节序转主机字节序
	conLen := binary.BigEndian.Uint32(lenBuf)
	if conLen == 0 {
		return
	}
	//读取并打印
	conBuf, err := readData(conn, conLen)
	fmt.Printf("server: %s", string(conBuf))
}

// 发送数据函数
func sendData(conn net.Conn, buf []byte) (n int) {
	conLen := make([]byte, 4)
	binary.BigEndian.PutUint32(conLen, uint32(len(buf)))
	data := append(conLen, buf...)
	n, _ = conn.Write(data)
	return
}

func readData(conn net.Conn, size uint32) ([]byte, error) {
	left := int(size)
	var data []byte
	for left > 0 {
		tmp := make([]byte, left)
		if n, err := conn.Read(tmp); err == nil {
			left -= n
			data = append(data, tmp...)
		} else {
			if err == io.EOF {
				data = append(data, tmp...)
				return data, err
			} else {
				return nil, err
			}
		}
	}
	return data, nil
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值