1.传输层

1.四/七层协议

①.物数/网/传/会表应

2.TCP/UDP

①.对比

特性 TCP UDP
连接方式 面向连接(三次握手/四次挥手) 无连接
可靠性 ✅是(重传、确认) ❌否(可以在任意一个环节丢包 比如包太大直接就被网卡丢了)
顺序 ✅是 (提供序列号) ❌否
流量控制 ✅是 滑动窗口 ❌否
拥塞控制 ✅是 RTT估算 ❌否
开销 较高 较低
适用场景 传输可靠数据,如 HTTP、FTP、SMTP 实时通信,如语音、视频、DNS

②.既然tcp这么好,为什么有些大厂选择使用udp然后自己实现上述的优点?

理由 解释
1.更低延迟 TCP 要三次握手建立连接,还会因丢包等待而产生延迟,而UDP是“发就完事”,适合对时延敏感的业务,如语音、游戏、直播
2.更可控的可靠性 大厂希望自己决定 何时重传、重传几次、是否丢弃,而不是被TCP的算法束缚
3.避免TCP拥塞控制带来的限速 TCP自带的拥塞控制在高丢包、高带宽场景(如全球视频分发)反而成为瓶颈
4.头部开销更小 TCP有大量字段(序号、确认号、窗口等),UDP 报头仅 8 字节,更节省带宽和处理开销
5.多路复用更灵活 TCP连接是内核管理的,每个连接一套状态;而基于 UDP 可以自己设计更轻量的“连接”系统,甚至用一个 socket 管理上万个“逻辑连接”
6.支持多播/广播 TCP 不支持广播/组播,但 UDP 可以,这对一些特定场景(如游戏局域网同步)非常有用
7.方便自定义协议 有些应用需要自定义帧格式、加密方式、压缩等,UDP更适合二开
3.三次握手/四次挥手

①.在三次握手和四次挥手中每一个分组的丢失都可以向上回溯到需要重发的地方,确保每个分组不会丢失
②.tcp的三次连接 (36页图2-5)
  三次握手的原因,a.收发双方确保收发双方的收发功能是正常的 b.确保每一个分组都能正常的到达对方
③.tcp的四次挥手(36页图2-5)
  四次挥手的原因,TCP允许半关闭状态 (例如cli主动关闭svr,svr的应用程序不会在收到数据,svr端内核还是会收到cli端内核的确认数据收到回应的,不然svr端发送的数据丢了svr也不知道)

4.time_wait

①.time_wait是两个MSL的时间,MSL包在网络中能存活的最长时间,MSL不是固定值
②.time_wait状态存在的理由
  a.确保tcp可靠的终止,例如cli主动关闭fd,cli最后发送的ACK丢失了,svr的FIN会重发,所以cli端必须维护这个状态来重发最后的ACK
  如果cli不维护这个状态,那么svr重传超次数以后回复一个RST来关闭这个连接
  b.如果没有这个这个状态,有新的相同ip端口的tcp化身产生,会出现老的数据出现在新的化身上,导致连接被RST
  所以需要一个time_wait的时间来阻止新的化身的出现

5.tcp缓存

①.tcp的缓存发送数据需要等待接受端的确认才会删除

2.TCP SOCKET编程

1.流程

①.流程图

②.作用
  bind: 绑定ip端口
  listen: 从关闭到监听状态
  accept: 取出完成3次握手的tcp连接没有就阻塞,accept出来的connfd套接字属性跟随listenfd套接字
      在accept后fork,listenfd和connfd套接字在父子进程之间共享
      父进程保留listenfd,子进程保留connfd,fd的引用计数>0时内核不会调用close关闭fd
close: 调用close以后意味着应用程序不能读写socket了,如果只是想关闭写可以调用shutdown
③.队列
  tcp维护着两个队列 已完成三次握手的连接队列和syn到达但没有完成握手的连接队列

2.syn cookies

①.syn攻击: 三次握手cli只发送sync建立连接 不发送最后的确定连接的ack,让服务器半连接的队列资源耗尽,影响正常的用户
②.syn cookies: 防止syn攻击的一种方式,是指服务器通过系列号+ip+port+一些其他的安全数据的hash运算加密得到,服务器在回复syn+ack的时候发送给客户端
        客户端在回复ack的时候要携带这个数据,服务器将ack的序列号-1进行校验

3.svr崩溃会发生什么

1.tcp通信时如果svr进程被终止了

①.svr内核会关闭进程所打开的socket fds
②.svr内核向cli发生FIN,cli的read返回0,cli的应用程序可以调用write,svr内核收到数据以后响应一个RST
③.cli应用程序往已收到RST的socket fd里面写数据的话,内核会向该进程发生SIGPIPE信号

2.tcp通信时如果服务器主机崩溃了

①.cli内核会重试发送数据到svr,超次数以后,返回超时
②.如果中间的路由器发现服务器不可达,则响应一个destination unreachable ICMP消息

3.服务器主机崩溃后重启

①.cli内核发送数据到svr,svr内核响应一个RST

4.服务器主机关机

①.init进程会向所有进程发送TERM信号,该信号可以被捕捉释放资源
②.一定时间以后,init进程会向仍在运行的进程发送KILL信号,之后就如同 #1

5.心跳

①.所以保证服务器崩掉后cli第一时间感知到的方式有心跳,不是依赖于tcp不可达

4.epoll select poll

1.select/poll

①.int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout)
②.int poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
③.select支持的fd数量上限是1024 poll是系统的上限
④.每次收集时间时,把需要关心的fds传给内核,内核遍历fds找出有对应未处理事件的fds返回

2.epoll

①.epoll把select poll的一个过程被拆分成三个部分(深入理解nginx 308-312页)
②.epoll_create 向内核申请一个简单的文件系统,返回对应句柄(无需从用户态到内核态的拷贝)
③.epoll_ctl 向epoll对象中添加fds需要监听的事件,红黑树单条fd插入是log(n)
④.epoll_wait 收集发生的事件,只需要遍历一个双向链表
⑤.epoll的回调,所有添加到epoll中的事件都会和设备(如网卡)驱动建立回调关系,使得这个事件被放在rdlllist双向链表中 (查找未处理事件时无需遍历所有fds)

3.ET/LT

①.当一个事件到来时,可以从epoll_wait调用中获取到这个事件,如果这次没有把这个事件的缓冲区处理完,在该套接字没有新的事件到来前
  再次调用epoll_wait,ET模式获取不到这个事件,LT模式只要缓冲区有数据就可以获取到这个事件
②.使用LT模式开发简单一些,不容易出错
③.在ET模式下,如果没有彻底地将缓冲区处理完,会导致缓冲区的用户数据得不到响应或者响应不及时
④.在写模式下LT,写完数据以后需要把事件从二叉树中移出来

文档更新时间: 2026-04-17 16:40   作者:morninglu