文章目录
前言
本文是再次学习,本篇博客是在原博客Posix API与网络协议栈的实现原理,内容上的补充与再学习
一、tcb与数据发送接受的深入理解
socket()的功能是在文件系统里分配fd,并且创建tcb。
tcb是啥记住这句话:在同一时刻,设备可能会产生多种数据需要分发给不同的设备,为了确保数据能够正确分发,TCP协议用一种叫做TCB(Transmission Control Block,传输控制块)的数据结构把发给不同设备的数据封装起来。一个TCB数据块包含了数据发送双方对应的socket信息以及拥有装载数据的缓冲区。在两个设备要建立连接发送数据之前,双方都必须要做一些准备工作,分配内存建立起TCB数据块就是连接建立前必须要做的准备工作。
可以看出一个连接,就包含一个tcb数据控制块,用对应fd通信的数据缓冲区。由此可见,每一个连接通信的时候,系统内核都会为这专门的连接分配一块对应的读缓冲区与写缓冲区(我以前一直以为,内核就是分配一块大的缓冲区,来供所有的通信连接使用的,现在看来不是这样,是每一个通信多有其对应的读缓冲区和写缓冲区)
二、全连接与半连接
- tcp三次握手完成时,全连接队列从半连接队列中查找该节点(tcb控制块),找到该节点,从半连接队列中删除,加到全连接队列中(全连接队列怎么在半连接队列中查找的匹配的节点的呢?通过数据包携带的五元组信息进行查找)(具体看原博客accept的操作)
- 当全连接队列中,有节点到来的时候,会释放信号出来,决定 accept能不能去取这个节点,也决定epoll可读该listenfd
三次握手,第三次(除了发送ack时,可以带数据也可以不带数据)
三、数据发送
为什么send返回正数,就说明成功?深入理解
send函数并没有发送数据的能力, send函数的作用仅仅是把应用程序的数据拷贝到发送端的内核缓冲区中, 只要有足够的空间, send函数就不会阻塞, 就会返回成功。 至于内核缓冲区中的数据是否发送, 什么时候发送, 那是协议栈的事情, 跟send函数没有半毛钱的关系。
数据的发送
(1)send把数据拷贝到内核协议栈中(send,通过fd找到对应的tcb,把数据拷贝到tcb对应的内核缓冲区里面)
send的三种情况:
1.单个send,信息一条一条发
2.循环send,send多次
send的数据都存在tcb的数据缓冲区里面,如果send的比较密集,可能会出现两个send的数据包合成一个数据包(粘包)
分包的方法:
(1)应用层协议头前面加上包的长度
对应recv的方法:读两次,第一次读取协议头上记录数据长度的数据,第二次读取协议头记录长度的那么长的数据
(2)为每一个包加上分隔符
对应的recv的方法:先读取固定长度,查询读取的内容中是否有分割符,如果有分隔符,保留分隔符后面的内容,继续读取,后合并内容
3.单个send,但是信息特别大(一个包需要多长send)
循环send,循环recv
.数据传输中间,网线断了,数据是怎么检测到的
(1)网卡重启,重新连接,系统会检测出来
(2)对于应用层数据:会有心跳包,定时发送心跳包进行检测
(3)客户端直接宕机;服务器也是通过探测机制(心跳检测)出来的
网络堵塞和客户端宕机,服务器是怎么判断出来的?
如果是堵塞,服务器发送探测包,客户端会回的,如果是宕机,不管怎么发,数据都不会回的
(探测机制心跳的学习)
四、断开连接
只分主动与被动,不分客户端与服务器
close()其实就是个send()只不过不包含任何数据(数据头有个fin),这个close有可能和send的数据一起发,并且回收fd
因此知道为啥recv=0是关闭了吧
如果出现了大量的close_wait如何解决?什么原因?
原因是recv=0时,未及时调用close
解决方法:
TCP通信过程中time_wait和close_wait产生过多的原因和解决方法