为什么说Tcp是面向字节流的以及(Tcp粘包问题、TCP/UDP对比、listen函数的backlog参数的意义)
为什么说Tcp是面向字节流的:
Tcp通信的本质是创建一个tcp的socket,同时就会对应的创建一个发送缓冲区和接收缓冲区。
Tcp协议的粘包问题
tcp协议将数据都放到了缓冲区中,而缓冲区中具体有几个报文是不确定的(因为发送的数据可大可小,可能一次报文就能把数据全部发过来,也可能一次只能发送半个 。。。),所以就类似于粘着的样子,对此我们就需要将数据分出来
对此就需解决上述问题,方案:
- 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可在。报文特殊字符(\r\n)。
- 对于定长的包, 保证每次都按固定大小读取即可; 例如上面的Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可。定长报文(确定报文长度)。
- 通过组合的策略解决问题
- 而对于UDP来说就不会有这种问题,因为UDP有明确的数据边界,并且站在应用层来看使用UDP的时候要么收到完整报文,要么不收。
TCP/UDP对比
用UDP如何实现可靠传输(经典面试题)
其实就是参考TCP协议实现可靠性的办法
- 超时重传,若隔一段时间后没收到应答,就重发数据
- 序列号,确保数据顺序,以及去除重复的数据
- 确认应答,确认数据确实发送过去
- …
连接其他机器指令:telnet
telnet + ip + port
TCP层面相关实验理解listen的第二个参数backlog
当没有accept,也是能完成三次握手进行连接(表示即使没有accept不会影响)
此时底层tcp会在自己的连接中,维护一个全连接队列,队列中有效节点的个数是有上限的(也就是listen的第二个参数backlog + 1个,超过后就会变成半连接状态)
如下图backlog的情况:
下图是backlog = 6 + 1 当出现第8个连接时就会出现 SYN_RECV状态
这是因为,Linux内核协议栈为一个tcp连接管理使用两个队列:
- 半链接队列(用于保存处于SYN_SENT和SYN_RECV状态的请求)
- 全链接队列(Accept队列)用于保存established状态,但是应用层没有调用accept取走的请求
此处的全连接队列的长度就会受到listen第二个参数backlog的影响,当该队列满后,就无法让后面的连接状态为established(建立),就会形成SYN_RECV(SYN连接请求)状态
该半连接状态并不会持续很久,它会等待一会尝试连接,当有全连接状态退出时,就能变成全连接状态,否则就会消失
作者:溟洵