怎么理解TCP传输协议是面向字节流的协议?
之所以说TCP是面向字节流的,UDP是面向报文流的,是因为操作系统对TCP和UDP协议的发送方的机制不同。
UDP是面向报文流的
用户通过UDP协议发送消息的时候,操作系统不会消息进行拆分,也就是每个UDP报文就是一个用户消息的边界,这样在接收方接收到UDP报文之后,读一个UDP报文就能读取到完整的用户消息。操作系统在接收到一个UDP报文之后就会插入到队列中,队列中的每一个元素就是一个UDP报文。
TCP是面向字节流的
当用户通过TCP协议发送消息的时候,消息可能会被操作系统分组成多个的TCP报文,也就是一个完整的用户消息被拆分为多个TCP报文进行传输,这时如果接收方不知道发送方发送的长度,就无法读出一个完整的用户消息。在发送端send之后,数据并没有真正的被发送出去,要取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。因此呢我们不认为一个TCP包对应一个用户消息,所以TCP是面向字节流的协议。
如何解决粘包?
一般有三种方式
1.固定长度的消息:每个用户消息都是固定长度的,比如规定一个消息的长度是 64 个字节,当接收方接满 64 个字节,就认为这个内容是一个完整且有效的消息。
2.特殊字符作为边界:可以在两个用户消息之间插入一个特殊的字符串,这样在接收方接收数据的时候,读取到这个特殊字符,就认为读取到了一个完整的消息。如HTTP,HTTP通过设置回车符、换行符作为HTTP报文的边界。但是当我们消息内容里面有这个字符的时候,需要进行转义,避免被接收方当作消息的边界点而解析到无效数据。
3.自定义消息结构:我们可以自定义一个消息结构,由包头和数据组成,包头的大小是固定的,包头里有一个字段来说明之后的数据有多大,当接收方接收到包头的大小后就解析包头的内容,然后就知道数据的长度了。然后接下来就继续读取数据直到读满数据的长度,这样就可以组装成一个完整的消息来处理了。