计算机网络学习笔记一、TCP协议

TCP协议

  从本篇文章开始,总结TCP协议相关的知识点。TCP协议相关内容可以分为5个部分:TCP报文,TCP连接,Socket编程,可靠传输机制和流量控制,拥塞控制。本篇文章记录TCP报文、TCP连接和Socket编程三个部分的内容。


1. TCP报文

  TCP协议是面向连接的、可靠的、基于字节流的数据传输协议。TCP协议在协议栈中位于IP协议的上一层,属于传输层协议。

1.1. TCP首部

  TCP首部占20到60个字节,即如果选项部分长度为0,TCP首部占20个字节。
在这里插入图片描述

2. TCP连接

  一个TCP连接由一对Socket(四元组:客户端ip:客户端port,服务器ip:服务器port)唯一标识。建立一个TCP连接是需要客户端与服务器达成三个信息的共识,包括socket、序列号、窗口大小。

  1. Socket:由IP地址和端口号组成

源地址和目的地址字段(32位)在IP头部,作用是通过ip协议发送报文给对方主机。
源端口和目的端口字段(16位)在TCP头部,作用是通过TCP协议把报文发送给目标主机的指定进程。


问题:一个服务器监听了一个端口,它TCP的最大连接数是多少?
  对ipv4来说,客户端最多 2 32 2^{32} 232个ip,端口号最多 2 16 2^{16} 216个,也就是说服务端单机最大TCP连接数为 2 48 2^{48} 248个。当然,服务端最大TCP连接数远远不能达到这个理论上限。
  原因可归纳为两点:

  1. 首先主要是文件描述符的限制,socket都是文件,可以在ulimit文件中配置文件描述符的个数。
  2. 另一个是内存限制,每个TCP连接都会占用一定内存,而服务器内存是有限的。

  1. 序列号:用来解决乱序问题。
  2. 窗口大小:用来做流量控制。

2.1. 三次握手

  建立一个TCP连接是需要客户端和服务器达成三个信息的共识,包括Socket、序列号和窗口大小。关于TCP三次握手可以简要描述为:客户端发送SYN,服务端发送SYN+ACK,客户端发送ACK。

  1. 客户端向服务器端发送请求报文,请求建立连接标志位SYN=1、发送序列号seq=x(随机),客户端进入SYN_SENT状态,等待服务器确认
  2. 服务器收到客户端请求,同意连接,发送确认报文,SYN=1,ACK=1,ack=x+1,序列号seq = y,服务器进入SYN_RECV状态。
  3. 客户端收到服务端报文,标志位ACK=1,发送确认号ack=y+1,seq= x+1,客户端和服务器进入ESTABLISHED状态。

  值得注意的一点是,第三次握手之前有了初始序列号,所以第三次握手时可以携带数据;前两次握手没有初始序列号,不能携带数据。总结来说,三次握手的过程就是客户端与服务器 初始化socket、序列号、窗口大小,并建立TCP连接的过程。

问题1:为什么是三次握手,而不是两次、四次握手?

  该问题可以分两个方面来回答,第一、为什么要使用三次握手。第二、为什么不能用两次握手。

  1. 首先回答第一个问题,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱(阻塞的SYN报文发送到服务端);同步双方初始序列号;避免浪费资源。
  2. 再回答问什么不能用两次握手,因为两次握手不能完成上述三个问题。
  3. 还有一点,两次握手容易形成死锁。如果服务器发送给客户端的确认信息丢失了,此时客户端以为还未建立连接,只会等待连接确认的应答分组,服务器发送的的分组超时后,又会发送同样的分组,就会形成死锁。
  4. 至于为什么不是四次握手,实际上四次握手也可以建立可靠连接,只是没有必要。

问题2:既然IP层会分片,为什么TCP层还需要MSS?
在这里插入图片描述

  1. IP层有一个超过MTU大小的数据要发送,那么IP层要进行分片,每个分片都会小于MTU。如果有一个IP分片丢失,整个IP报文所有分片都得重传,这是因为IP层没有超时重传机制,它是由传输层的TCP来负责超时和重传的。
  2. 当接收方发现TCP报文的某一片丢失后,则不会响应ACK给对方,那么发送TCP超时后,就会重发整个报文,因此可得出IP层进行分片传输效率并不高。
  3. TCP协议建立连接时,通常要协商双方MSS,TCP层发现数据超过MSS时,会先进行分片,MSS形成的IP包也不会大于MTU。TCP分片机制,进行重发时也是以MSS为单位的。

问题3:什么是SYN攻击?如何避免SYN攻击?

  攻击者伪造不同IP地址的SYN报文,服务端每接收到一个SYN报文,就进入SYN_RCVD状态,但服务端发送出去的ACK+SYN报文,无法得到未知IP主机的ACK应答,久而久之就会占满服务器的SYN接收队列,使得服务器不能为正常用户服务。


问题4:Linux内核SYN队列和Accept队列工作原理

  1. 当服务端接收到客户端的SYN报文时,会将其加入内核SYN队列。
  2. 接着发送SYN+ACK给客户端,等待客户端回应ACK报文。
  3. 服务端接收到ACK报文后,从SYN队列移除,放入Accept队列。 应用通过调用accept(),从Accept队列取出连接,如果应用程序过慢时,会导致Accept队列被占满。

2.2. 四次挥手

  关于TCP四次挥手可以简要描述为:客户端发送FIN,服务端发送ACK,服务端发送FIN,客户端发送ACK。

  1. 客户端发送连接释放报文,表示请求断开连接。断开连接标志位FIN=1、序列号为seq=u,客户端进入FIN_WAIT_1阶段。
  2. 服务端收到释放连接报文,发送确认报文。确认标志ACK=1、ack=u+1,带上序列号seq=v,服务器进入CLOSED_WAIT状态。客户端收到服务器端的ACK回复以后,与服务器进行最后的数据传输,而这段数据传输的时间,在客户端处于FIN_WAIT_2状态。
  3. 服务器将最后数据发送完毕,发出连接释放报文,FIN=1、ACK=1,ack=u+1,由于期间服务器又发送了一些数据seq=w,服务器进入LAST_ACK状态。
  4. 客户端收到服务器的连接释放报文后,确认标志ACK=1,ack=w+1,自己序列号为u+1。同时自己进入一个TIME_WAIT的等待时间,等待结束后客户端关闭连接,服务器收到客户端的ACK后会关闭连接。

问题1:为什么建立连接要三次挥手而断开连接要四次挥手?
  建立连接时ACK和SYN是放在一个报文里来发送。关闭连接时,被关闭的一方可能还有未发完的数据需要先发送ACK,等数据发送完,再发送FIN报文表示同意断开连接,因此这里的ACK报文和FIN报文是分开发送的。

问题2:TIME_WAIT状态

  1. 主动关闭连接的,才有TIME_WAIT状态。
  2. MSL(Maximum Segment Lifetime),报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
  3. TIME_WAIT为2MSL的合理解释在于,网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,一来一回需等待2倍时间,Linux系统中2MSL默认是60秒。

问题3: 为什么最后一次挥手后还要等待2MSL的时间?

  1. 保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
  2. 防止旧连接的数据包出现在新连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

问题4:优化TIME_WAIT

  1. 一个四元组唯一标识一个TCP连接,理论上服务端可以建立很多连接。服务端确实只监听一个端口,但是会把连接扔给处理线程,而线程池可能处理不了那么多一直不断开的连接。所以系统资源被占满时,服务端出现大量TIME_WAIT,会导致处理不过来新的连接。
  2. 优化方式。第一,net.ipv4.tcp_tw_reusetcp_timestamps;第二,net.ipv4.tcp_max_tw_buckets;第三,使用SO_LINGER

3. Socket编程

3.1. 步骤

服务端

  1. 初始化socket,得到文件描述符。
  2. 调用bind,绑定ip地址和端口号。
  3. listen,进行监听。
  4. accept,等待客户端连接。

客户端

  1. connect,向服务器端的地址和端口号发起连接请求。
  2. 客户端拿到服务器调用accept返回的文件描述符。
  3. 客户端调用write写入数据,服务端调用read读取数据。
  4. 客户端断开连接时调用close关闭连接;服务端调用read读数据时,会读到EOF,处理完数据后,调用close关闭连接。

注意: 服务端调用accept时,连接成功,会返回一个已经完成连接的socket,用来传输数据。

  1. 监听和真正用来传输数据的是两个socket,一个叫作监听socket,一个叫作已完成连接的socket。
  2. 成功建立连接后,双方通过read和write读写数据,就像是往一个文件流里写内容一样。

问题一: listen函数

  1. Linux内核中会维护两个队列:一个是未完成连接的队列(SYN队列),即处于SYN_RCVD状态的队列;另一个是已完成连接的队列(Accept队列),即处于ESTABLISHED状态的队列。

  2. int listen(sockfd, backlog)函数中backlog参数在早期linux系统中,通常指SYN队列大小;在Linux内核2.2之后,backlog变成accept队列,现在通常认为backlog是Accept队列。

问题二:connect函数和accept函数

  1. 客户端connect成功返回是在第二次握手后。
  2. 服务端accept成功返回是在三次握手成功后。

4. TCP和UPD的区别

TCP和UDP是TCP/IP体系结构中运输层的两个协议。UDP全称是用户数据报协议,TCP全称是传输控制协议。

  1. 是否基于连接?
    TCP是面向连接的协议,UDP是无连接的协议。也就是说,UDP在发送数据前无需建立连接。
  2. 是否可靠?
    TCP是可靠的,UDP是不可靠的。TCP通过校验和,重传控制,滑动窗口,确认应答,可靠传输等机制保证了消息的按序到达,尽可能保证消息的可靠性;UDP是尽最大努力交付,数据包可能会以任何可能的顺序到达。
  3. 实时性
    UDP具有很好的实时性,工作效率比TCP高。
  4. 协议首部
    TCP首部消耗20字节,UDP首部消耗8字节。
  5. 是否支持一对多
    TCP连接只能是点对点的;UDP支持一对一,一对多,多对一,多对多。
  6. 应用场景
    TCP应用在HTTP,HTTPS,FTP,SMTP,Telnets;
    UDP用在DNS,DHCP,SNMP,RIP,VOIP等。

5. 子网掩码

5.1. 子网掩码的作用是什么?

  子网掩码是用来划分子网的,ip&子网掩码=网络地址
  Host ID 为全1 的IP 地址为广播地址。广播地址可以向该网段内所有主机发送消息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值