《Linux高性能服务器编程》阅读笔记:
TCP和UDP是传输层的两个主要协议。TCP相对于UDP来说,是面向连接、字节流和可靠传输。
(1) 面向连接
使用TCP通信的双方必须先建立起连接,然后才能开始数据的读写。建立连接后双方的系统内核会为该连接分配必要的系统资源,用来管理连接的状态和传输在连接上的数据。TCP连接是全双工的,即双方的读写操作可以在同一连接上执行。数据交换完成后,通信双方都需要关闭该连接以释放系统资源。TCP的连接是一对一的,所以需要广播或多播(目标是多个主机)的应用程序不能使用TCP服务,而是使用无连接的UDP服务。
(2) 字节流服务
跟字节流服务经常放在一起的还有数据报服务,它们的区别在形式上体现为通信双方是否需要指向相同次数的读/写操作。对于使用字节流服务的TCP的通信双方来讲,当发送端应用程序连续执行多次写操作时,(系统内核空间的)TCP模块会先将这些数据放入(内核空间的)TCP发送缓冲区中。当TCP模块需要真正发出数据时,发送缓冲区中待发送的数据会被封装成一个或者多个TCP报文段发出。由此可见,TCP模块发送出的TCP报文段的个数跟与应用程序执行写操作的次数之间没有固定的数量关系。
同理,TCP接收端具有接收缓冲区,当接收端接收到一个或者多个TCP报文段之后,TCP模块将报文携带的发送端应用程序数据按照TCP报文段的序号放入该缓冲区,并通知应用程序来读取数据。接收端应用程序可以一次性将接收缓冲区的内容读出,也可以分多次读取,这取决于用户指定的应用程序读缓冲区的大小。由此可见,TCP模块接收到的TCP报文段的个数与应用程序执行读操作的次数之间没有固定的数量关系。
综上所述,发送端的写操作和接收端的读操作之间没有固定的数量关系,这就是字节流服务,应用程序对数据的发送和接收是没有边界限制的。
UDP则不然,它是属于数据报服务,发送端应用程序每执行一次写操作,UDP模块就会将之封装成一个UDP数据报并发送出去,接收端必须及时针对每一个UDP数据报执行读操作,否则就会出现丢包现象,另外,如果接收端没有足够的应用程序来读取数据,UDP数据就会被截断。
(3) 可靠传输
a. TCP采用发送应答机制,即发送端发送的每个TCP报文段都必须得到接收方的应答后才认为TCP报文段传输成功。
b. TCP具有超时重传机制,发送端在发出一个TCP报文段之后启动定时器,若在超时时间范围后还没收到接收端的应答信号,它将重发该数据报文。
c. TCP报文段最终是以IP数据报发送的,IP数据报到达接收端可能是重复/乱序的,所以TCP模块还会对接收到的TCP报文段重排、整理,再交付应用程序。
UDP协议与IP协议一致,提供的是不可靠服务,数据确认和超时重传都需要在上层应用实现。
(1) 16位的源端口号/目的端口号
告知目的机器报文段来自哪里(源端口号)以及传给传递给哪个上层协议或者应用程序(目的端口号)。进行TCP通讯时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名端口号。在Linux系统中,一些知名服务使用的端口号定义在/etc/services中。
(2) 32位序号
一次TCP通讯(从TCP连接的建立到断开)整个过程中,一个传输方向上的字节流的每一个报文的编号。例如主机A和主机B进行TCP通讯,A发送给B的第一个TCP报文中,序号值就被系统设置为某个随机值(ISN, Initial Sequence Number),在该传输方向(A->B)的后续TCP报文的序号子将被系统设置为ISN加上该报文所携带的第一个字节在整个字节流的偏移。假设某个TCP报文段传输的数据是整个字节流中的第1024~2048字节,那么该报文的序号值为ISN+1025,下一个报文为ISN+2049。
(3) 32位的确认号
用于对对方发来的TCP报文段的响应,其值为收到的TCP报文段的序号值加1。
(4) 4位头部长度
标志该TCP头部具有多长,单位为字(4字节),可见TCP头部最长为60字节。
(5) 6位保留
(6) 6个标志位
① URG: 表示紧急指针是否有效
② ACK: 表示确认号是否有效(携带ACK标志的TCP报文段称为确认报文段)
③ PSH: 提示接收端应用程序要立即从TCP接收缓冲区读走数据,以腾出空间接收后续的数据。(若应用程序不读走数据,数据会一直留在TCP模块的接收缓冲区)
④ RST: 表示要求对方重新建立连接(携带RST标志的TCP报文段为复位报文段)
⑤ SYN: 表示请求建立一个连接(携带SYN标志的TCP报文段称为同步报文段)
⑥ FIN: 表示通知对方要关闭连接(携带FIN标志的TCP报文段为结束报文段)
(7) 16位窗口大小
这是TCP流量控制的一个手段。此处的窗口指的是接收通告窗口,用于告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
(8) 16位校验和
由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。作为TCP可靠传输的重要保障,这个校验不仅包括TCP头部,也包括数据部分。
(9) 16位紧急指针
存放着一个正的偏移值,该值加上当前报文的序号将得到紧急指针,紧急指针处存放的是紧急数据,是发送端向接收端发送紧急数据的方法。
(10) TCP头部选项
TCP头部选项存放着至多40字节的可变长的可选信息(因为TCP头部结构最长为60字节,除去前面的固定部分)。典型的TCP头部选项结构结构如下:
(1) Kind: 说明选项类型
(2) Length: (如果有的话)指定该选项的总长度
(3) Info: (如果有的话)选项的具体信息
(1) Kind=0
选项表结束选项
(2) Kind=1
空操作选项,无特殊意义,一般用于将TCP选项的总长度填充为4字节的整数倍
(3) Kind=2
最大报文段长度选项,TCP连接初始化时,通信双方使用此选项来协商最大报文长度(Max Segment Size, MMS)。
(4) Kind=3
窗口扩大因子。TCP连接初始化时,通信双方使用该选项来协商接收通告窗口的扩大因子。在TCP头部中,接收通告窗口大小用16位表示,故最大为65535字节。实际上TCP模块允许的接收通告窗口大小远不止这个数,窗口扩大因子解决了此问题。假设接收通告窗口大小为N,扩大因子为M,那么TCP报文段的实际接收通告窗口大小为N乘2^M,即N左移M位。M的取值范围是0~14,/proc/sys/net/ipv4/tcp_window_scaling
内核变量用来启用或关闭窗口扩大因子选项。
MSS选项与窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但是同步报文段不执行窗口的扩大操作,即同步报文段的接收通告窗口大小就是该TCP报文段的实际接收通告窗口大小。当连接建立完毕后,每个数据传输方向的窗口扩大因子就固定不变了。
(5) Kind=4
选择性确认(Selective Acknowledgment, SACK)选项。TCP通信时若某个TCP报文段丢失则TCP模块会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段可能会被重复发送,从而降低TCP性能。SACK技术使TCP模块只重新发送丢失的TCP报文段,不用发送所有未被确认的TCP报文段。选择性确认用于连接初始化时表示是否支持SACK技术。/proc/sys/net/ipv4/tcp_sack内核变量用于启动/关闭选择性确认选项。
(6) Kind=5
SACK实际工作的选项,该选项的参数部分用于告诉发送方本端已收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。
(7) Kind=8
时间戳选项,该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time, RTT)的方法,从而为TCP流量控制提供重要信息。/proc/sys/net/ipv4/tcp_timestamps内核变量用于启动/关闭时间戳选项
在 IP协议协议–IP头部信息中利用tcpdump观察了基于TCP的telnet服务的数据包的IP头部数据,该数据同样适用于观察TCP数据报:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
IP 127.0.0.1.44626 > 127.0.0.1.23: Flags [S], seq 3041640326, win 43690, options [mss 65495,sackOK,TS val 4701824 ecr 0,nop,wscale 7], length 0
0x0000: 4510 003c 0106 4000 4006 3ba4 7f00 0001
0x0010: 7f00 0001 ae52 0017 b54b bf86 0000 0000
0x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a
0x0030: 0047 be80 0000 0000 0103 0307
(1) Flags[S]表示该TCP报文段含SYN标志,即它是一个同步报文段。若TCP报文段包含其他标志,标志的首字母也显示在Flags后的方括号内。
(2) seq是序号值,该报文段是从127.0.0.1.44626到127.0.0.1.23的传输方向上的第一个TCP报文段,所以序号值seq也就是此次通信过程中该传输方向的ISN值,且它没有针对对方送来的TCP报文段的确认值(因为尚未接收到任何对方发送来的TCP报文段)。
(3) win是接收通告窗口的大小,因为这是一个同步报文段,所以win值指的是实际接收通告窗口的大小(不是窗口扩大因子参与计算后的结果)
(4) options是TCP头部的可选项信息,其具体内容在方括号中,mss是发送端(客户端)通告的最大报文长度。ifconfig可查看回路接口的MTU为146字节,即TCP报文段的MSS为16336-40=16396字节。因为这是一次TCP通信的第一个TCP报文段,所以针对对方的时间戳的应答为0;紧接着是nop是一个空操作;wscale指出发送端使用的窗口扩大因子为6。
下来呈现的十六进制的格式,共60字节: 前20字节是IP头部,后40字节是TCP头部,即从21字节看起:
0xae52 源端口号
0x0017 目的端口号
0xb54bbf86 序号
0x00000000 确认号
0xa TCP头部长度为10个32位,即40字节
0x002 设置了SYN标志
0xaaaa 接收通告窗口的大小
0xfe30 头部校验和
0x0000 没设置DRG标志,即紧急指针值无意义
0x0204 最大报文段长度选项的Kind值和length值
0xffd7 最大报文段长度
0x0402 允许SACK选项
0x080a 时间戳选项的kind值和length值
0x0047be80 时间戳
0x00000000 应答时间戳
0x01 空操作选项
0x0303 窗口扩大因子选项的kind值和length值
0x07 窗口扩大因子