一.概述
在互联网时代,整体的服务都是通过网络进行。网络如果出现瓶颈也会影响服务体验,更严重的会导致服务不可用。
了解网络也是能够在搬砖的工作中,能够识别一些坑,并在出现问题的时候能够较快的定位。
二.tcp/ip协议分层
一般有7层协议,四层协议。
用的比较多的是四层协议,这个协议也是我们大多数人关注的。
四层协议发送与接收详细每层数据结构:
可以看到上层复用下层能力,发送的时候从上层往下依次将数据包装,接收的时候则依次解封。如应用层是http协议往传办理层提交数据,传输层会加上自已的数据规范=tcp首部(传输层数据)+http数据。
三.tcp协议
1.tcp三次握手连接
客户端与服务器通过三次握手进行连接,图如下:
这里为什么是3次,而不是2次或1次。这个本质上是一个概率问题。
3次能保证刚好客户端与服务端都有请求与响应。一定程度上确定了网络及双方服务的可用性。
2.tcp四次握手关闭
这里有time_wait状态主要是是以下2点考虑:
a.让客户端的响应有机会重试,因为客户端在第4步响应服务器的时候可能出现丢包,所以需要有时间做重试。
b.在客户端关闭连接后,后续又重用了这个端口,网络上的包有可能由于网络原因后面又到达了这个端口。此时如果时间很短就会导致这些包是这个新连接的。
3.tcp要解的几个问题
tcp从参与者来看,有:发送方,网络媒介,接收方三个角色。
滑动窗口解法乱序,重传及接收方处理不过来的问题。
拥塞窗口解法网络阻塞问题。
而在整个网络交互过程中会有以下几个问题:
a.乱序
即发送方发送了a,b两个包,接收方可能接收的顺序是b,a。
策略:
定义包id,自增,接收方确认的时候根据包id顺序确认。同时接收方应用层根据这个id来进行确认,如果出现发送方发了1,2,3三个包,接收方接收到了1,3,这时接收方只能确认1.
b.包超时
发送方发送包时,由于网络或接收方问题会出现包的超时。
策略:
超时重传。
超时重传的策略有:
发送方定时超时重传:这个可以采样网络rt,计算平均rt。
接收方对于未收到的包重发3次期望接收的ack。
接收方将已接收地图发给发送方,由发送方判断,如已接收1,3. 这样发送方就知道2丢了,要重发。
c.接收端处理不过来
接收端处理不过来,一般是发送方发送太快。
d.不能将网络整个塞满
这个一个解法就是碰到丢包问题,就将拥塞窗口减小。
tcp一般会减少原先窗口的n/2. 然后后续每收到一个包就加1.
这个解法缺点也很明显,即网络带宽可能是好的,但是少数包丢失是因为接收方问题,造成了误判。另外一方面在出现丢包问题时,多余的包可能已经将网络中中间设备填满了,这时整体网络环境已经很糟糕了,太迟了。
另一个解法就是用tcp的bbr算法。
4.socket编程
服务端与客户端通信,要经过连接,收发数据,关闭三个生命周期。
相应双方交互的流程图:
服务器与客户端交互是一个四元组:客户端ip及端口,服务端ip及端口。
这里需要注意的是一个客户端连接和后续的读写是2个socket,服务器端就是2个文件描述符。这么做原因是整体按socket的生命周期去看,连接属于一个连接范围的功能,后续读写切开去看,职责相对比较内聚。
而服务器理论上能够打开socket的数量是:2的48次方。
但由于服务端采用的io线程模型是哪个方案,会决定到底数量是多少。
如果是同步阻塞的io模型,由于每个客户端过来连接都需要一个线程,因此这个上限取决于操作系统能够创建的线程上限。
如果是同步非阻塞io复用,selector模式:则上限是1024. 这个是由于每个服务器端口监听的socket都会放到一个fd_set文件描述符数组中。这个模式还有缺点:每次都需要将fd_set从内核拷到用户态然后selector线程再看哪个事件就绪了。性能差。
在selector后面,出现了epoll。epoll是同步非阻塞,但优化点在于要监听的socket不存数组了,而是会新建一个epoll文件描述符,这个文件描述符会指向一个红黑树,红黑树每个结点就是要监听事件对应的socket。这样即解决了fd_set数组1024上限的问题,也解决了查询哪个socket有事件的性能问题,并且不用拷贝完整的socket监听对象到用户态。
epoll图:
5.常见问题
- tcp time wait在哪个端?有什么问题?怎么解?
在关闭发起方,通常是客户端。问题会是出现大量的time wait导致客户端无可用连接。
解法:优先长连接,tcp_tw_recycle(不建议,NAT情况下会导致连接失败),tcp_tw_reuse。
tcp_tw_recycle问题是在NAT网络下不同客户端使用同一个IP,而不同客户端时间不一样就会导致客户端B连接不上(由于客户端A用了这个连接,时间又比B慢)。
tcp_tw_reuse则是限制只会重用time wait状态且到time_wait状态时间超过1秒的连接。本质上是减少概率发生。
- tcp为什么是三次握手,不是二次,或四次。
2次:超时情况下会会建立多个无效连接及死锁情况。多余无效连接:例:客户端发起连接sync包,超时后客户端再发起一个sync。但由于只是二次握手此时服务端建立了2个连接。死锁例:服务端收到sync就建立好连接开始传数据,但由时客户端还没收到服务端的ack此时客户端会丢弃这些包,但服务端认为超时会重发。