传输层:TCP、UDP

  1. 绝大多数网络应用使用TCP或UDP,而这两者又使用网络层协议:IPv4或IPv6,也可以绕过传输层直接使用IPv4或IPv6,但这种技术(往往被称为原始套接字)却极少使用
  2. Linux使用一种称为SOCK_PACKET的特殊套接字类型提供对于数据链路的访问
  3. TCP和UDP都是既可以使用IPv4,也可以使用IPv6
  4. UDP缺乏可靠性,不保证UDP数据报会到达其最终目的地,不保证各个数据报的先后顺序跨网络后保持不变,也不保证每个数据报只到达一次
  5. 如果一个UDP数据报到达了其最终目的地,但是校验和检测发现有错误或者该数据报在网络传输途中被丢弃了,它就无法被投递给UDP套接字,也不会被源端自动重传
  6. 每个UDP数据报都有一个长度,如果数据报正确到达其目的地,那么该数据报的长度将随数据一道传递给接收端应用进程,因此我们就能知道数据报的边界
  7. 由于UDP是无连接的,因此一个UDP客户端可以使用同一个套接字发送不同的数据报给不同的服务器,服务器也可以用同一个UDP套接字从若干不同的客户端接收数据报
  8. TCP是面向连接的,通过确认应答、超时重传以及序列号等保证其可靠性,并且TCP含有用于动态估计客户和服务器之间的往返时间(round-trip time,RTT)的算法,TCP还提供流量控制,同时TCP连接是全双工的,UDP也可以是全双工的
  9. 常用TCP选项:
    (1)MSS选项:MSS代表最大分节大小,也就是本次连接中每个TCP分节中愿意接受的最大数据量,发送端TCP使用接收端的MSS值作为所发送分节的最大大小
    (2)窗口规模选项,TCP连接任何一端能够通告对端的最大窗口大小为65535,因为在TCP首部中相应的字段占16位,不过为了更大的窗口大小,TCP中有新选项代表窗口扩大(左移)的位数
    (3)时间戳选项,这个选项对于高速网络连接是必要的,它可以防止由失而复现的分组可能造成的数据损坏
  10. 在TCP连接断开时,服务器发送FIN的ACK之后和服务器主动发送FIN之间,从被动关闭到主动关闭一端的数据流动是可能的,这称为半关闭
  11. 执行主动关闭的端在最后关闭前会经历TIME_WAIT状态,该端点停留在这个状态的持续时间最长是最长分节生命期(maximum segment lifetime,MSL)的两倍,MSL是任何IP数据报能够在因特网中存活的最长时间。
  12. TIME_WAIT状态有两个存在的理由:
    (1)可靠地实现TCP全双工连接的终止,假设最后的ACK丢失,那么被动关闭方需要主动关闭一方来重传这个ACK
    (2)允许老的重复分节在网络中消逝,因为如果没有这个TIME_WAIT时间,如果在连接断开后立马建立相同连接,那么之前在网络中还未消逝的分节就会被误认为是新的分节导致错误
  13. UDP和TCP都使用16位整数的端口号来区分各个进程,UDP和TCP定义了一组众所周知的端口,用于标识众所周知的服务,比如ftp的TCP端口就是21.客户端通常使用短期存活的临时端口,这些端口号通常由传输层协议自动赋予客户并且保证其在所在主机的唯一性
  14. 众所周知的端口号为0~1023,已登记的端口号为1024~49151,临时端口为49152~65535
  15. 一个TCP连接的套接字对是一个定义该连接的两个端点的四元组:本地IP地址、本地TCP端口号、外地IP地址、外地TCP端口号,套接字对唯一标识一个网络上的每个TCP连接,而标识每个端点的两个值(IP地址和端口号)通常称为一个套接字
  16. 在服务器主机上需要区分监听套接字和已连接套接字,已连接套接字和监听套接字使用相同的本地端口,在多宿服务器主机上,连接一旦建立,已连接套接字的本地地址随即填入
  17. IPv4数据报的最大大小为65535字节,包括首部,因为其长度占据16位,IPv6数据报最大大小为65575字节,包括40字节的首部。IPv4要求的最小链路MTU(最大传输单元)为68字节,包括20字节的固定长度首部和最大40字节的选项部分拼接最小的片段(IPv4首部中片段偏移字段为8个字节为单位),IPv6要求的最小链路MTU为1280个字节,不过链路如果有分片和重组功能的话IPv6也能运行在MTU小于1280个字节的链路上
  18. 两个主机之间最小的MTU称为路径MTU,1500字节的以太网MTU是当今常见的路径MTU,两个主机之间相反方向的MTU可以不一致,因为路径可以不同
  19. 当一个IP数据报大于相应链路的MTU时,IPv4和IPv6都将执行分片,这些片段在最终到达目的地之前通常都不会被重组,IPv4的主机和路由器都可以对数据报执行分片,而IPv6只有主机可以分片,路由器不能对转发的数据进行分片,只能对自己产生的数据报进行分片
  20. 如果IPv4首部中的“不分片(don’t fragment)”位(即DF位)被设置,那么无论主机还是路由器都不能分片,如果路由器接收到一个超过其MTU且DF位被设置的IPv4数据报时将产生一个ICMPv4(目的地不可达)错误信息
  21. IPv4的DF位和IPv6的隐含DF位可用于路径MTU发现,路径MTU发现对于IPv4是可选的,但是IPv6要么支持它,要么总是以小于最小MTU发送数据报。
  22. IPv4和IPv6都定义了最小重组缓冲区大小,它是IPv4或IPv6的任何实现都必须保证支持的最小数据报大小,其值对于IPv4为576字节,对于IPv6为1500字节
  23. 每一个TCP套接字都有一个发送缓冲区,我们可以使用SO_SNDBUF套接字选项来更改该缓冲区的大小。当某个应用进程调用write的时候,内核从该应用进程的缓冲区中复制所有数据到所写套接字的缓冲区,如果发送缓冲区容不下该应用进程的所有数据,该应用进程将会睡眠,在阻塞模式下直到应用进程缓冲区中的数据都复制到套接字发送缓冲区时内核才从write系统调用返回。因此一个TCP套接字的write调用返回成功仅仅表示我们可以使用原来的应用进程缓冲区,并不表明对端的TCP或应用已接收到数据
  24. TCP必须为已发送的数据保留一个副本,直到它被对端确认为止
  25. 每一个UDP套接字都有一个发送缓冲区,我们可以使用SO_SNDBUF套接字选项来更改该缓冲区的大小。不过它仅仅是可写到该套接字的UDP数据报的大小上限。如果一个应用进程写一个大于套接字发送缓冲区大小的数据报,内核将返回该进程一个EMSGSIZE错误。既然UDP是不可靠的,它不必保存应用数据的一个副本,因此无需一个真正的发送缓冲区。
  26. 如果某个UDP应用进程发送大数据报,那么它们相比TCP应用数据更有可能被分片,因为TCP会把应用数据划分成MSS大小的块,而UDP却没有对等的手段。
  27. 从写一个UDP套接字的write调用成功返回表示所写的数据报或其所有片段已被加入数据链路层的输出队列。如果该对列没有足够的空间存放该数据报或它的某个片段,内核通常会返回一个ENOBUFS错误给它的应用进程。
  28. ping和traceroute是使用ICMP协议实现的网络诊断应用。traceroute自行构造UDP分组来发送并读取所引发的ICMP应答。
  29. 在cmd中输入命令tracert ip地址可以查看到达该ip地址经过的所有路由地址。

Nagle算法

  1. 在TCP传输数据流中,存在两种类型的TCP报文段,一种包含成块数据(通常是满长度的,携带一个报文段最多容纳的字节数),另一种则包含交互数据(通常只有携带几个字节数据)。对于成块数据的报文段,TCP采用正常的流程发送即可,因为数据利用率很高。而对于交互数据的报文段,数据利用率就显得很低,在网络环境不好的情况下容易加重网络负担。所以TCP必须对交互数据单独处理
  2. nagle算法用于处理小报文段(微小分组)的发送问题,nagle算法的核心思想是允许网络中最多只能有一个小分组被发送,而待发送的其它小分组会被重新分组成一个”较大的”小分组,等收到上一个小分组的应答后再发送
  3. 虽然nagle算法可以减少网络中小分组的个数,但是对于那些需要实时预览的通讯程序而言,客户端可能需要不断发送更新数据并得到服务器的响应,这种情况下nagle算法会造成客户端明显的延迟,所以需要禁用nagle算法,我们可以使用将套接字描述符设置TCP_NODELAY选项可以禁止nagle算法

套接字选项

  1. 我们可以使用getsockoptsetsockopt函数来获取或设置套接字选项,套接字选项大致可以分为两大基本类型:一是启用或禁止某个特性的二元选项(称为标志选项),二是取得并返回我们可以设置或检查的特定值选项(称为值选项)
  2. accept一直要到TCP层完成三路握手后才会给服务器返回已连接套接字,而有的套接字选项是由TCP已连接套接字从监听套接字继承而来的,比如SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、TCP_NODELAY等选项,因此要确保这些选项是给已连接套接字选项设置的话就必须先给监听套接字设置该选项
  3. 应用进程在发送广播数据包时必须设置SO_BROADCAST选项,不然会返回EACCES错误(仅限UDP,因此TCP无法进行广播)
  4. SO_DEBUG选项仅由TCP支持,当开启本选项时,内核将为TCP在该套接字发送和接收的所有分组保留详细跟踪信息。这些信息保留在内核的某个环形缓冲区中,并可使用trpt程序进行检查
  5. SO_DONTROUTE选项规定外出的分组将绕过底层协议的正常路由机制,路由守护进程(routed和gated)经常使用本选项来绕过路由表(路由表不正确的情况下),以强制将分组从特定接口送出
  6. 当一个套接字上发生错误时,内核将该套接字的名为so_error的变量设为标准的Unix Exxx值中的一个。进程可以通过访问SO_ERROR套接字选项来获取so_error的值,由getsockopt返回的整数值就是该套接字的待处理错误,so_error随后由内核复位为0,SO_ERROR套接字选项只能获取不能设置
  7. 给一个TCP套接字设置保持存活SO_KEEPALIVE选项后,如果2小时内在该套接字的任一方向上都没有数据交换,TCP就自动给对端发送一个保持存活探测分节,这个分节对端必须响应,它会导致以下三种情况:
    (1)对端以期望的ACK回应,一切正常,在经过毫无动静的2小时后再次发出另一探测分节
    (2)对端以RST响应,它告知本端TCP:对端已崩溃且已重新启动,该套接字的待处理错误被置为ECONNRESET,套接字本身关闭
    (3)对端对保持存活探测分节没有任何响应,TCP将另外发送8个探测分节,每个间隔75秒,试图得到一个响应,TCP在发出第一个探测分节后11分15秒内若没有得到任何响应则放弃,套接字关闭
    本选项的功能是检测对端主机是否崩溃或变得不可达,但是也可能会终止存活的连接,因此在第三种情况下路由器崩溃15分钟就会切断连接。本选项一般由服务器使用,因为客户端如果连接掉线、崩溃或者断电,服务器将一直等待客户端的请求而一直阻塞