TCP学习笔记

1.TCP是面向连接的,面向字节流的,首部20字节,首部中没有数据的总长度,但是TCP的接收端会有滑动窗口,并且接收端需要发送确认号,TCP得到此次数据的长度通过接收函数确定数据的长度。

  1. 关于TCP粘包问题
    粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程)
    1 发送端需要等缓冲区满才发送出去,造成粘包
    2 接收方不及时接收缓冲区的包,造成多个包接收

应用程序写数据一般都是写到缓存区,然后TCP会去缓存区里面去取,然后给数据封装TCP的头部信息,然后将数据发过去,如果客户端写得太快,连续两次将data1和data2写进去了缓存区,这时候TCP会去缓存区里面去取数据,取到data1和data2,然后直接将两个数据封装一个头部,发过去给服务端了,服务端解出来就可能是data1和data2的数据。在应用层的表现的可能是客户端发送一个int类型的数据,因为粘包的存在,在服务端看到就不是原来的那个int了。

  1. tcpdump命令可以进行抓包

  2. 关于建立连接的初始序列号
    初始序列号是系统为我们选择的一个序列号ISN,ISN选择随机数的原因:
    1)ISN随着时间而变化,每一个连接都有着不同的ISN,这样保证每个连接的初始序列号都不一样,那么每一次的序列号之后发送的数据的序列号也不同,那么上一次连接中的延迟的分组在以后被传送,接收方就会认为是超时的分组,就不会对它进行解释了。如果说两个初始序列号一致了,那么前一个连接中滞留的包又发到了接收端,接收端会对该滞留的包进行解释,这样前一个连接里面的包会干扰新的连接里面的包。
    2)防止IP欺骗的注入式攻击(不改变通信双方的通信,只是在会话中插入一些伪装ip的Tcp包)

  3. 连接建立的超时
    很多情况导致无法建立连接、客户端会间隔一段时间发送SYN,伯克利系统建立一个新的连接的最长时间限制为75秒

  4. 半关闭是为了让客户端告诉服务端我已经没有数据要发送给你了,但是你可以传数据给我
    shutdown
    举个例子,将一些数据传给服务器,服务器进行排序完成,然后将排序后的结果发给客户的,这个时候客户端发送完数据之后进行发送FIN关闭连接,但这个时候服务端还可以发送数据给客户端。

  5. 2MSL等待状态
    每个具体的TCP报文段的最大生存时间
    执行主动关闭的一端再发回最后一个FIN的确认号给另一端后,会进入TIME_WAIT状态,为了防止最后一个FIN包的确认包的丢失,比如过了MSL时间,服务器还没有收到最后一个ACK,这个时候已经过去了MSL时间,由于此时服务器还没有收到FIN包的ACK,服务器就会重发FIN包,这时候客户端接收到FIN包后,再次发送确认号ACK给服务端,才会进入CLOSED状态。也就是,2MSL时间是一来一回的报文段的最大生存时间。

  6. TIME_WAIT
    理论上:默认情况下,在处于2MSL等待时间期间,定义这个连接的socket(客户端IP和端口号,服务器IP,端口号)不能再使用。
    实现时:大多数TCP实现要更为严格,在2MSL等待期间,四元组中使用的本地端口在默认情况下不能再被使用(而不只是这个四元组不能使用)
    API中实现时避免这种情况:使用SO_REUSEADDR选项。
    设置SO_REUSEADDR选项可以允许一个进程(客户端或者服务端)重新使用仍处于2MSL等待的端口(本地IP+PORT),但是,TCP不允许使用同一个四元组(本地ip,本地port,目的ip,目的port)。
    但是:大多数伯克利实现中,服务器执行主动关闭,在2MSL内,同一四元组可以被重用。

  7. 平静时间
    如果说处于等待2MSL的主机出现故障,如果说允许立即客户端采用之前的端口号重新对服务器建立一个新的连接,那么在前一个连接中滞留的报文会干扰新的连接,服务端可能对上一个连接中的数据进行解析,所以说,必须等待2MSL等前一个连接中的报文全部失效,然后建立新的连接。

  8. 复位报文段
    RST报文不会导致另一端产生任何响应,另一端不用确认
    1) 当连接请求到达时,目的端口没有进程正在监听
    2)异常终止一个连接
    3) 检查半打开连接:半连接的原因是一方异常终止连接而另一方却不知道,比如客户端和服务端建立连接后,服务器突然掉电,然后进行了重启,客户端再给服务端发送一个请求的时候,会收到服务器的RST报文,显示连接已被服务器的主机终止。

  9. 同时打开连接和同时关闭连接
    TCP允许客户端和服务器端同时打开连接,但是只会建立一条连接,而不是两条连接。
    1)两端几乎同时发SYN,进入SYN_SEND状态;
    2)当每一端收到SYN的时候,状态变为SYN_RCVD,同时它们都再发SYN+ACK(对对端的SYN进行确认);
    3)双方收到SYN及相应的ACK时,状态变为ESTABLISHED。
    同时关闭:
    1)应用层发出关闭命令,两端都从ESTABLISHED变为FIN_WAIT_1,这将导致双方各发一个FIN;
    2)收到对方的FIN后变为CLOSING状态,并发送最后的ACK;
    3)收到最后的ACK后,双方状态均变为TIME_WAIT。

  10. TCP 的连接队列,积压值
    当服务器正忙的时候,TCP如何处理新来的连接?
    正在等待连接请求的一端会有一个固定长度的连接队列,队列中存放的是已经经过了三次握手的连接,但是还没有被应用层接受(等待ACCEPT的返回)。
    应用层将指明该队列的最大长达,这个值就是积压值。积压值范围为0-5,对应着1-8的最大排队连接数。
    现在backlog用来确定已完成队列(完成三次握手等待accept)的长度,而不再是已完成队列和未完成连接队列之和(linux 2.2之前)。
    未完成队列(incomplete connection queue)的长度现在由/proc/sys/net/ipv4/tcp_max_syn_backlog设置,在现在大多数最新linux内核都是默认512,这个设置有效的前提是系统的syncookies功能被禁用,如果系统的syncookies功能被启用,那么这个设置是无效的。Syncookies是在内核编译的时候设置的,查看syncookies是否启动:
    cat /proc/sys/net/ipv4/tcp_syncookies
    如果是“1”说明已启用,为“0”说明未启用。
    那么为syncookies是做什么的呢,为什么它会和未完成队列有关系。简单的说它是为防范SYN Flood攻击的设计。

注意: 当队列已满的时候,服务端不会理会收到的SYN,也不会发送RST报文给客户端,因为如果发送了RST报文给客户端的话,就相当于对客户端说我想拒绝你的连接,客户端主动打开将被废弃,所以还是让客户端定时重传SYN给服务端好了,什么时候连接队列空了一点了,客户端就可以建立连接了,这样至少不会被服务端剥夺建立连接的请求。

13.坚定计时器/坚持定时器
前提:
TCP通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来进行流量控制。如果窗口大小为 0会发生什么情况呢?这将有效地阻止发送方传送数据,直到窗口变为非0为止。
TCP不对ACK报文段进行确认, TCP只确认那些包含有数据的ACK报文段。
如果一个确认丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非 0的窗口),而发送方在等待允许它继续发送数据的窗口更新。为防止这种死锁情况的发生,发送方使用一个坚持定时器 (persist timer)来周期性地向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为窗口探查 (window probe)。
当服务端这边滑动窗口大小win=0的时候,服务端这个时候会给客户端发送win=0的通告窗口,但是过了很久,服务端没有发送一个窗口告知客户端它的情况,这时候客户端怎么知道服务端的窗口的情况呢。客户端采用坚定计时器,如果说在计时器的时间到之前服务端都没有发送窗口更新跟客户端,客户端就会发送窗口探测报文给服务器,窗口探测报文包括一个字节的数据,服务器就会回复它现在的win的大小是多少。TCP从不放弃发送窗口探测,这些探测每隔60s发送一次,直到窗口被打开,或者应用进程连接被终止。

14.糊涂窗口综合症

服务端还没有等到win变大了,就发一个窗口更新报文给客户端,然后客户端就发送比较小的数据,给服务器来处理,浪费了带宽,如果很多客户端同时向服务端发送这样的小包的,浪费服务器的资源而且也浪费了带宽。
解决办法:
1) 接收方不通告小窗口,等窗口变大了再发送窗口更新,或者客户端发小包的过来的时候,服务端直接返回win=0,这样客户端可以暂时不发送数据
2) 在客户端也可以进行攒数据,也就是等待缓存区的数据足够大再发送给服务器

15.TCP的保活定时器
TCP的保活定时器为了避免客户端和服务器端空闲的连接一直保持着,比如其中一方突然关闭了,而它们之间没有数据交换,这样的话,一方就不知道另一方不存在了,然后还一直保持连接。
服务端和客户端如果都需要了解双方是不是存在,可以都设置保活定时器。

如果两个小时之内,给定连接没有任何动作,服务端会向客户端发一个探测报文,客户端必然处于一下4个状态之一
(1) 客户主机依然正常运行,并且从服务器端可达
客户端的TCP响应正常,从而服务器端知道对方是正常的。保活定时器会在两小时以后继续触发。
(2) 客户主机已经崩溃,并且关闭或者正在重新启动
客户端的TCP没有响应,服务器没有收到对探测包的响应,此后每隔75s发送探测报文,一共发送9次。
socket函数会返回-1,errno设置为ETIMEDOUT,表示连接超时。
(3) 客户主机已经崩溃,并且重新启动了
客户端的TCP发送RST,服务器端收到后关闭此连接。
socket函数会返回-1,errno设置为ECONNRESET,表示连接被对端复位了。
(4) 客户主机依然正常运行,但是从服务器不可达
双方的反应和第二种是一样的,因为服务器不能区分对端异常与中间链路异常。
socket函数会返回-1,errno设置为EHOSTUNREACH,表示对端不可达。

Q:TCP是如何发送Keepalive探测报文的?
A:分两种情况。

  1. 有新的数据段可供发送,且对端接收窗口还没被塞满。发送新的数据段,来作为探测包。
  2. 没有新的数据段可供发送,或者对端的接收窗口满了。发送序号为snd_una - 1、长度为0的ACK包作为探测包,这是一个序号过时的报文。snd_una: first byte we want an ack for,所以snd_una - 1序号的字节已经被确认过了。对端收到此包后会发送一个ACK响应,如此一来本端就能够知道对端是否还活着、接收窗口是否打开了。
    Q:如果网络中有发送且未确认的数据包、或者发送队列不为空时,保活定时器不起作用了,
    岂不是不能够检测到对端的异常了?
    A:可以使用TCP_USER_TIMEOUT,显式的指定当发送数据多久后还没有得到响应,就判定连接超时,
    从而主动关闭连接。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值