likes
comments
collection
share

不背八股!!!为什么说TCP是基于字节流的?

作者站长头像
站长
· 阅读数 6

什么是基于字节流?

我们都知道TCP的一大特点就是基于字节流,从字面意思上来说,TCP在传输报文的时候并不是一个个报文传输的,而是一个个字节传输的。

因为TCP无消息边界的TCP不保留消息的边界,也不提供消息开始和结束的标记,发送方将数据按照字节流进行发送,接收方需要根据应用层协议的规定来划分消息。

并且TCP提供了可靠的数据流传输,接收方无法确定消息边界,并且消息以流的形式进行传输,发送方可以把这个报文的一部分先传输过去,剩下的部分和下一次要传输的内容一起传输过去,这也是会造成拆包粘包的原因之一。

为什么TCP基于字节流?

那么为什么TCP可以用这种方式进行数据的传输呢?

其实TCP基于字节流这个与TCP的可靠性又分不开,因为TCP是可靠的,所以可以面向字节流进行传输,因为UDP是不可靠的,所以UDP只能单个报文进行传输。

举个例子,假如我们想要发送一大段视频给其他人。

那么这个视频最后的传输依旧是一大串0101的字节码,假如其中的一段是这样的,每一行是一个报文

不背八股!!!为什么说TCP是基于字节流的?

那么因为TCP是可靠的,所以它知道自己无论如何发送,最终对方收到的都是可靠的报文,何为可靠?

不错误、不重复、不丢失、按序,这个时候它就没有必要追求一个个报文发送,而是考虑到IO、缓冲区、网络状况等因素,追求效率的最大化。如果某一次发送的数据丢失或者失败,那么重传即可,并不会影响到其他的数据。

所以TCP可能按照下面几种格式进行发送

不背八股!!!为什么说TCP是基于字节流的?

不背八股!!!为什么说TCP是基于字节流的?

不背八股!!!为什么说TCP是基于字节流的?

为什么UDP基于报文?

也是一样的道理,因为UDP不可靠,所以它只能一个报文一个报文传输,这样在传输失败的时候,仅需进行一个报文的重传,而不需要重传一大段数据。

想一想,如果UDP不可靠,但是又是基于字节流的发送,那么会出现什么问题?

当一次发送的数据因为过大而失败时,不可靠的UDP会不断重试这个过程,但是无法确保这段数据重传成功,会对其他的数据造成影响。 但是有可能在整个数据都发送完毕的时候依旧没有重试成功,这时候接受方收到的数据就是不完整的,轻则丢帧丢数据,重则整个视频都无法打开。

所以UDP的发送格式只能按照下列格式,一个报文一个报文发送。

不背八股!!!为什么说TCP是基于字节流的?

TCP粘包

粘包是指在数据传输过程中,接收方收到的数据和发送方的逐个报文并不完全一致,而是"粘"在一起的。

代码示例

服务端代码如下

func main() {
	readTimes := 100
	listener, err := net.Listen("tcp", "127.0.0.1:10211")
	if err != nil {
		log.Fatal("tcp listen failed, err:", err)
		return
	}
	defer listener.Close()
	for i := 0; i < readTimes; i++ {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("accept msg failed,err:", err)
			continue
		}
		readMsg(conn)
		time.Sleep(10 * time.Millisecond)
	}
	return
}

func readMsg(conn net.Conn) {
	defer conn.Close()
	msg := make([]byte, 100)
	for {
		cnt, err := conn.Read(msg)
		if err == io.EOF {
			fmt.Println("finish read")
			return
		}
		if err != nil {
			fmt.Println("read msg failed, err:", err)
			break
		}
		recvMsg := string(msg[:cnt])
		fmt.Println("receive msg:", recvMsg)
	}
}

客户端代码如下

func main() {
	const sendTimes = 100
	conn, err := net.Dial("tcp", "127.0.0.1:10211")
	if err != nil {
		log.Fatal("dial tcp failed, err:", err)
		return
	}
	defer conn.Close()
	for i := 0; i < sendTimes; i++ {
		msg := "welcome to follow blogger anneshaertrecord"
		conn.Write([]byte(msg))
	}
	return
}

最终效果

receive msg: welcome to follow blogger anneshaertrecord
receive msg: welcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follo
receive msg: w blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger annesh
receive msg: aertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcom
receive msg: e to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blog
receive msg: ger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertre
receive msg: cordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to f
receive msg: ollow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger an
receive msg: neshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwe
receive msg: lcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow 
receive msg: blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshae
receive msg: rtrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome
receive msg: to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogge
receive msg: r anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertreco
receive msg: rdwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to fol
receive msg: low blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anne
receive msg: shaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelc
receive msg: ome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow bl
receive msg: ogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaert
receive msg: recordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to
receive msg:  follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger
receive msg: anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecord
receive msg: welcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follo
receive msg: w blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger annesh
receive msg: aertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcom
receive msg: e to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blog
receive msg: ger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertre
receive msg: cordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to f
receive msg: ollow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger an
receive msg: neshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwe
receive msg: lcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow
receive msg: blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshae
receive msg: rtrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome
receive msg: to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogge
receive msg: r anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertreco
receive msg: rdwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to fol
receive msg: low blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anne
receive msg: shaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelc
receive msg: ome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow bl
receive msg: ogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaert
receive msg: recordwelcome to follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to
receive msg:  follow blogger anneshaertrecordwelcome to follow blogger anneshaertrecordwelcome to follow blogger
receive msg: anneshaertrecordwelcome to follow blogger anneshaertrecord
finish read

可以很明显的看到,并不是每一条消息都为welcome to follow blogger anneshaertrecord,有很多消息"粘"在一起了。

为什么会出现TCP粘包?

有很多导致TCP粘包出现的原因,前面我们提到过的流式传输以及无消息边界是最主要的原因。除此之外还有一些其他层面原因导致TCP粘包。

  • Nagle算法:这是一种优化TCP效率的算法,通过延迟小数据包的发送,将它们合并成一个较大的数据包,从而提高效率和网络利用率。
  • 接收方缓冲区限制:当发送方连续发送多个数据包,而接收方的缓冲区大小不足以及时处理这些数据包,也会导致粘包。
  • 网络延迟或者拥塞:数据包可能乱序到达,需要在接收方缓冲区重新拼装。

如何解决TCP粘包?

1.消息边界

通过给消息的不同部分设置分隔符,来实现消息边界的确立,这样接收方就能知道一条数据报应该在哪里结束。比如HTTP报文其实就是通过在不同部分设置换行来进行边界的确立的。

2.定长消息 这种方式下,消息的长度是固定的,如果一条数据报超过这个长度,则进行拆分发送,这种方式不灵活,并且一个大的数据报可能需要拆分多次,效率低。

3.头部字段

在消息头部添加一个字段,用于标识本条消息的长度。接收方首先读取长度字段,然后根据长度读取相应字节数的数据,这样来确保每条消息都能够被正确地分割。

结语

本文向大家介绍了TCP为什么是基于字节流的,以及TCP粘包相关内容。创作不易,如果有收获欢迎点赞、评论、收藏,您的支持就是我最大的动力。