tcp解决粘包

代码链接: https://github.com/wjw1758548031/Golang/blob/master/tcp.go

tcp协议相对于udp协议的差别
TCP UDP
是否连接 面向连接 面向非连接
传输可靠性 可靠 不可靠
应用场合 少量数据 传输大量数据
速度 慢 快
根据这个确定运用场合就好。

tcp粘包原因:
tcp协议的优化导致而成,也就是在多少毫秒内需要等到多少大小的缓存内容,才会进行发送,在
高并发模式下,很容易造成这样的问题,也就会几个包的内容黏在一起,甚至一个包只粘了一半的
内容在上个包的结尾。

解决方式:
第一种:在客户端进行操作,强行发送或者是等待一个缓存的确认时间,一个个发送,但是这样
tcp的优化就没太大效果,不建议使用。
第二种:在服务端进行校验:
发送时 设置 body 以及 head
body:序列化内容
head:需要记录内容大小。
head的字节是需要固定好的,比如int64是最大容量8位字节,那么就记录8位字节,好让服务端获取。
此时设置好这些的话,服务端就能根据头部具体的取到自己的内容,然后再做些校验等操作。
文字讲的不够详细,实在不明,直接看代码,代码简洁明了。

package main

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

//int64 序列化最大容量为8
var (
	headLen  = int64(8)
	bodyByte = make([]byte, 0)
)

//tcp双项通道
func main() {
	go ServerBase()
	time.Sleep(1 * time.Second) //网速卡的话自己调整,可能在没创建完就已经连接
	go ClientBase()
	time.Sleep(1 * time.Hour)
}

func ServerBase() {
	fmt.Println("Starting the server...")
	//create listener
	listener, err := net.Listen("tcp", "127.0.0.1:50000")
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		return
	}

	// listen and accept connections from clients:
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Error accepting:", err.Error())
			return
		}
		//create a goroutine for each request.
		go doServerStuff(conn)
	}

}

type TcpDate struct {
	Header int64
	Body   string
}

func NewTcpDate(Body string) TcpDate {
	return TcpDate{Header: int64(len(Body)), Body: Body}
}

func (t TcpDate) EncodeHeader() []byte {
	b_buf := new(bytes.Buffer)
	if err := binary.Write(b_buf, binary.BigEndian, &t.Header); err != nil {
		panic(err)
	}
	return b_buf.Bytes()
}

func (t TcpDate) Encode() []byte {
	return []byte(string(t.EncodeHeader()) + t.Body)
}

func (t *TcpDate) Decode(data []byte,  endLen int64) (bool, []byte) {
	if endLen > int64(len(data)-1){
		return false,data
	}
	head := data[:endLen]
	b_buf := new(bytes.Buffer)
	b_buf.Write(head)
	if err := binary.Read(b_buf, binary.BigEndian, &t.Header); err != nil {
		panic(err)
	}
	body := data[endLen : endLen+t.Header]
	t.Body = string(body)
	fmt.Println(t.Body)
	return true, data[endLen+t.Header:]
}

func doServerStuff(conn net.Conn) {
	fmt.Println("new connection:", conn.LocalAddr())
	for {
		data := TcpDate{}
		//自己定义长度,如果数据过多。。。。
		buf := make([]byte, 1024)
		bufLen, err := conn.Read(buf)
		if err != nil {
			fmt.Println("Error reading:", err.Error())
			return
		}
		bodyByte = []byte(fmt.Sprintf("%v%v",string(bodyByte) , string(buf[:bufLen])))
		for ; ; {
			var is bool
			is,bodyByte = data.Decode(bodyByte, headLen)
			if !is  {
				break
			}
		}
		/*len,_ := strconv.Atoi(strings.TrimPrefix(string(head),"ceshi:"))
		fmt.Println("data:",string(buf[100:len]))*/
		conn.Write([]byte("已收到"))

	}
}

func ClientBase() {
	//open connection:
	conn, err := net.Dial("tcp", "127.0.0.1:50000")
	if err != nil {
		fmt.Println("Error dial:", err.Error())
		return
	}
	//send info to server until Quit
	for i := 0; i < 100; i++ {
		go func() {
			item := struct {
				Name1 string
				Name2 string
				Name3 string
				Name4 string
				Name5 string
			}{}

			body, _ := json.Marshal(item)
			data := NewTcpDate(string(body))
			dataByte := data.Encode()
			_, err := conn.Write(dataByte)
			if err != nil {
				fmt.Println("Error Write:", err.Error())
				return
			}

			//这里就是简单的接收
			buf := make([]byte, 1024)
			length, err := conn.Read(buf)
			if err != nil {
				fmt.Println("Error reading:", err.Error())
				return
			}
			fmt.Println("Receive data from server:", string(buf[:length]))
		}()
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值