4层网络
- 链路层
ARP协议、RARP协议 - 网络层
IP协议。 - 传输层
TCP、UDP协议 - 应用层
http、https 、FTP、POP3、SMTP协议
TCP 传输控制协议
面向连接的可靠的字节流传输层协议,通信双方传输信息之前必须先建立连接,TCP报文头的内容如下:
TCP报文头
字节在 20 - 24 直接,主要组成为:
- 源端口:2字节
- 目的端口:2字节
- 序号seq:4字节
- 确认号ack:4字节
- 窗口:2字节
- 同步SYN:只能等于 0 或 1,建立TCP连接是用到
- ACK:为1时,确认序号有效
seq字段:握手挥手都需要
这个seq字段在握手和挥手都有使用,这个字段是干嘛的?其实这个字段是一个报文序号字段,主要是做报文做一个版本号,和ack配合使用,比如:
- 我给你一个报文,seq = 4 ,你收到报文后,告诉我ack = 4+1,表示你收到了seq = 4的报文,我继续给seq =5的报文
这个字段主要是用来解决网络包乱序(reordering)问题,其实在基于滑动窗口的传输数据报文时,也用到了序号问题,这个字段也可以用上。
ack字段和ACK的区别
首先我们能看到其实有2个ack,一个是 acknowledgement numbe,另一个是ACK,大小写来区分,这是不同的含义的,我们来一起看看:
- ack
表示确认号,表示期待接收的下一个序列号,已经接受了的数据字节的序列号seq+1,ack = seq+1,seq的报文我已经接受到了,你继续发送seq+1的报文吧,这个字段只有在ACK位被启用的时候才有效,即ACK=1。 - ACK
确认,使得确认号有效。
SYN
SYN是在建立连接时用到的同步信号,一般是双方建立连接的时候才会带上,之后就再也不带上,它会影响什么吗?SYN=0是不消耗序列号的。在三次握手过程中,第三次握手是没有SYN=1的, 所以三次握手结束后, 客户端下一个发送的报文中 seq 依旧是 x+1。参考:TCP三次握手中SYN,ACK,seq ack的含义
FIN
finish结束,该报文段的发送方已经结束向对方发送数据。
三次握手
三次握手是TCP建立连接时的三次发送报文操作,
- 为什么不是两次?
如果就两次报文交互,第一次A给B发送的报文被堵塞了,A又发送了一次,这次被B收到了,然后数据传输完毕,此时,第一次发的报文恰好到了,B又给A方法送了一次,这次B就会一直等待A给B 发送消息,但其实这次连接已经结束了,但浪费B的资源。主要是防止已失效的连接请求报文突然又传到了B。
怎么理解三次握手:
- 1、第一次握手,服务端知道了客户端是可以发送消息的
- 2、第二次握手,客户端知道服务端是可以接受和发送消息的
- 3、第三次握手,服务端知道客户端是可以接受消息的
3次握手就很清楚的了解了客户端、服务端关于接受、发送消息的情况。
什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…
SYN攻击
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
常见的防御 SYN 攻击的方法有如下几种:
- 缩短超时(SYN Timeout)时间
- 增加最大半连接数
- 过滤网关防护
- SYN cookies技术
四次挥手
4次挥手主要分为两个阶段,每个阶段2次握手,第一次阶段:
- 客户端发送给服务端,我要关闭了,你同意嘛?。客户端FIN-WAIT-1状态。
- 服务器告诉客户端:我同意,你关闭吧。客户端FIN-WAIT-2状态。服务端:进入CLOSE-WAIT(关闭等待)状态
这个阶段主要是:客户端到服务端的关闭。
第二个阶段:
- 服务器告诉客户端:我也要关闭了,你同意嘛?进入了LAST-ACK(最后确认)状态
- 客户端告诉服务器:我同意,TIME-WAIT(等待时间)状态,经过2MSL后服务器才能进入到CLOSED状态
为什么要经过2MSL ?
-
1、了保证客户机最后发送的那个ACK报文段能够到达服务器
如果ACK没到服务器,服务器会再次发送FIN报文,也就是第三次握手,客户端收到FIN报文后,客户端还会再次发送ACK请求,会再等待2ML时间。 -
2、防止“已失效的连接请求报文段”出现在本连接中。
这里有一个很有意思的问题,服务端发送FIN报文和收到ACK报文最多要花费2ML时间,也就是说在第三次握手中客户端收到FIN报文的时候,FIN报文花费的时间最多1ML,此时此刻,服务端等待了1ML,它会干嘛?它会认为报文丢了,会重传FIN报文,所以网络中又多一个FIN报文,但没结束,等待客户端发送ACK给服务端时,最多花费1ML时间,此时此刻服务端又发送了一个BIN报文,又多了一个报文,梳理一下最差的情况就是:
- 1、B服务端发送FIN报文,称为报文1
- 2、最差情况下,报文1经过1ML抵达A客户端,此时B重传又发送FIN报文,我们叫报文2
- 3、A发送ACK报文,A开始等待时间称为X
- 4、最差情况下,1ML抵达B,此时B重传又发送FIN报文,我们叫报文3,然后B 关闭close掉,同时A也收到了报文2,此时A又开始发送ACK报文,我们叫报文4
当然网络中报文丢了才是最最差情况,但讨论此种情况对本文分析流程没有太多作用,这里忽略,此时网络中还有报文3和报文4,我们来假设极端情况:
- 如果 X=1ML,此时B最后一个报文3的前一个报文很快就达到A,跟A发送ACK是一致的,然后B关闭前发送了报文3,它需要1ML,这个时候X=1ML,而且还有报文3还需要1ML抵达A,但A已经超过了时间,不处理报文3
- 如果 X=2ML,那就可以覆盖上述场景。
综上所述,最极端的情况:
- B最后一个报文3的前一个报文很早就到了A,此时A也发送ACK报文,1ML时间抵达B
- B收到ACK报文的时候,也发送了一个报文,需要1ML时间抵达A
在这种极端情况下,TIME_WAIT至少需要持续2MSL时长,这2个MSL中的第一个MSL是为了等自己发出去的最后一个ACK从网络中消失,而第二MSL是为了等在对端收到ACK之前的一刹那可能重传的FIN报文从网络中消失。这里主要的TCP 报文头 字段是Fin和ACK
- 发送方和接收方都需要FIN和ACK
FIN:表示终止连接
ACK:为1时,确认序号有效
传输协议
- 停止等待协议
- 滑动窗口协议
拥塞的解决办法
1、慢开始和拥塞避免
1988年提出,拥塞窗口cwnd是从小到大的不断增加,每次经过一个传输轮次,窗口是原来的两倍,但是有一个门限阈值ssthresh,主要分情况:
cwnd < ssthresh : 慢开始算法
cwnd > ssthresh :拥塞避免算法
cwnd = ssthresh :既可以使用慢开始算法,也可以使用拥塞避免算法
-
如何发现网络开始拥塞了?
没有按一个返回RTT时间内收到所有分组的确认回复, -
拥塞开始了,如何处理?
就把门限阈值ssthresh 设置为cwnd的一半,并把cwnd = 1;让路由器有足够的时间把队列处理完毕。 -
拥塞避免:加法增大
1988年提出,思想:经历了一个往返时间RTT就让cwnd +1 ,不是翻倍,线性缓慢增长。
拥塞避免、拥塞处理也是一样的。
2、 快重传- 快恢复
主要解决失序分组一直没有确认的问题,这样会导致拥塞算法不停的调试,比如:发送 M1、M2、M3、M4这四个报文分组,如果M3一直没有确认回复,其他三个已经有确认回复了,那么会导致超时,拥塞算法会认为网络拥塞了,但其实没有拥塞,如何处理这种情况?那就是快重传:
- M3的确认回复没有到发送方,发送方会继续发送M5、M6,接受方收到M5、M6 也会继续发送确认回复M2
- 发送方 连续 收到三次重复确认,就立即重传未收到的报文M3,
这样方式可以提高20%的吞吐量。
快恢复则是配合快重传的,当发生快重传时,执行快恢复算法,乘法减半:门限阈值ssthresh减少为自己的一半,cwnd值为门限阈值ssthresh减半后的值,并采用拥塞避免算法。
长链接、短链接
- 长链接表示无数据时也不会断开链接,有保活机制,每次都会探测客户端
一次建立链接,长久使用 - 短链接:传输数据结束就直接断开
使用完就归还
http 1.0一般就指短连接,smtp,pop3,telnet这种就可以认为是长连接。一般的网络游戏应用都是长连接,建立链接要3次握手,断开链接要4次,因此资源消耗和时间消耗是很普遍的,所以不同业务场景也会用到这种长短链接,
资料:https://www.cnkirito.moe/tcp-talk/
http 与RPC的问题讲解。
加密 解密等问题
一般都使用非对称加密算法,该算法给提供公钥、密钥,公钥是公开的,每个人都知道,密钥是自己保留的,不能公开,公开了就起不到加密的问题,特性:
- 公钥可以解密密钥加密的内容
这个过程也叫数字签名,主要解决确定身份的问题。 - 密钥可以解密公钥加密的内容
对传输内容解密
因为密钥是唯一的,我用你的公钥解密出来就可以确定身份是你,但是一般不用密钥来对传输内容加密,因为公钥是公开的,大家就都可以解密你的内容,所以A和B通信,A、B就必须持有对方的公钥,还存在一个问题:
- 公钥那么多,有人对我说,这个公钥是你的,但其实被改了,是另一个人的,我发的发的内容都用他的公钥加密的,被他都知道了,我怎么确保公钥可以对的上你的人?
于是出现了第三方权威机构CA,他们保管了所有的机构的密钥加密后的签名,公钥解密后就可以知道是谁的了,这样别人篡改了也可以知道对方是谁。
一台服务器可以连接多少个TCP 连接
在tcp应用中,server事先在某个固定端口监听,client主动发起连接,经过三路握手后建立tcp连接。那么对单机,其最大并发tcp连接数是多少?
-
client最大tcp连接数
client每次发起tcp连接请求时,除非绑定端口,通常会让系统选取一个空闲的本地端口(local port),该端口是独占的,不能和其他tcp连接共享。tcp端口的数据类型是unsigned short,因此本地端口个数最大只有65536(2的16 次方),端口0有特殊含义,不能使用,这样可用端口最多只有65535,所以在全部作为client端的情况下,最大tcp连接数为65535,这些连接可以连到不同的server ip。 -
server最大tcp连接数
server通常固定在某个本地端口上监听,等待client的连接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的情况下,即使server端有多个ip,本地监听端口也是独占的,因此server端tcp连接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,因此最大tcp连接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp连接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp连接数约为2的48次方。
上面给出的是理论上的单机最大连接数,在实际环境中,受到机器资源、操作系统等的限制,特别是sever端,其最大并发tcp连接数远不能达到理论上限。在unix/linux下限制连接数的主要因素是内存和允许的文件描述符个数(每个tcp连接都要占用一定内存,每个socket就是一个文件描述符),另外1024以下的端口通常为保留端口。在默认2.6内核配置下,经过试验,每个socket占用内存在15~20k之间。
对server端,通过增加内存、修改最大文件描述符个数等参数,单机最大并发TCP连接数超过10万 是没问题的,国外 Urban Airship 公司在产品环境中已做到 50 万并发 。在实际应用中,对大规模网络应用,还需要考虑C10K 问题。但实际上一台机器最多连接多少TCP连接呢?
-
可打开文件数量
最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制,在linux下编写网络服务器程序每一个tcp连接都要占一个文件描述符,一旦这个文件描述符使用完了,新的连接到来返回给我们的错误是“Socket/File:Can’t open so many files”。这时你需要明白操作系统对可以打开的最大文件数的限制。 -
端口号限制
基于上述我们从以下4点总结:
- 对mysql用户可同时打开文件数设置为10240个;
- 将Linux系统可同时打开文件数设置为1000000个(一定要大于对用户的同时打开文件数限制);
- 将Linux系统对最大追踪的TCP连接数限制为20000个(但是,建议设置为10240;因为对mysql用户的同时打开文件数已经限制在10240个;且较小的值可以节省内存);
- 将linux系统端口范围配置为1024~30000(可以支持60000个以上连接,不建议修改;默认已经支持20000个以上连接);
综合上述四点,TCP连接数限制在10140个。
这10240个文件中还得除去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听 socket,进程间通讯的unix域socket等文件。
参考博客
长连接、短连接的讲解
网络长短连接
服务器最大TCP连接数及调优汇总
网络面试题
为什么tcp的TIME_WAIT状态要维持2MSL
为什么TCP4次挥手时等待为2MSL?