//1. IPv4 : 网际协议版本4。使用32位地址。IPv4给TCP、UDP、SCTP、ICMP、IGMP提供分组递送服务。 IPv6 : 网际协议版本6。使用128位地址。IPv6给TCP、UDP、SCTP、ICMPv6提供分组递送服务。 TCP/IP协议概况:
IP协议:
//2. UDP简介: UDP是一个简单的传输层协议,应用进程往一个UDP套接字写入一个消息,该消息随后被封装到一个UDP数据报中, 该UDP数据包又被封装入一个IP数据报,然后发送至目的地。UDP不保证其数据报会到达其最终目的地, 不保证各个数据报的先后顺序跨网路后保持不变,也不保证每个数据报只到达一次。 如果一个数据报到达了最终目的地,但是校验和校验发现有错误,或者该数据报在传输途中被丢失,他就无法被投递给套接字, 也不会被源端重传,若想保证一个数据报到达其目的地,需要添加:来自对端的确认、本端的超时重传等特性。 每个UDP数据报都有一个长度,如果一个数据报正确地到达其目的地,那么该数据报的长度将随数据一起传递给接收端程序。 相对应的TCP是一个字节流协议,没有任何的记录边界。 我们也说UDP提供无连接的服务,因为UDP客户与服务器之间不必存在任何长期的关系,一个UDP客户可以创建一个套接字并发送 一个数据报给一个给定的服务器,然后立即用同一个套接字发送另一个数据报给另一个服务器。同样的,一个UDP服务器可以用 同一个UDP套接字从若干个不同的客户接收数据报。 UDP协议:
//3. TCP简介: TCP提供客户与服务器之间的连接。TCP提供了可靠性,当TCP向另一端发送数据时,他要求对端返回一个确认。 如果没有收到确认,TCP就自动重传数据并等待更长时间,当多次重传失败后,TCP才放弃,如此在尝试发送数据上所花费的时间一般为4-10分钟(取决于实现) TCP含有动态估算服务器和客户之间的往返时间的算法,以便他知道等待一个确认需要多少时间。 TCP通过给其中每个字节关联一个序列号对所发送的数据进行排序。举例: 假设一个应用写2048字节到一个TCP套接字,导致TCP发送2个分节(TCP传递给IP的数据单元):第一个分节所含数据序列号1-1024, 第二个分节所含数据的分节号1024-2048。如果这些分节非顺序到达,接收端TCP将先根据他们的序列号进行排序,再把结果数据传递给接收应用。 如果接收端TCP收到来自对端的重复数据,他可以根据序列号判定数据是重复的,从而丢弃数据。 TCP提供流量控制,TCP总是告知对端在任何时刻他一次能够从对端接受多少字节的数据,称为通告窗口。在任何时刻,该窗口指出接收缓冲区当前可用的空间量, 从而确保发送端发送的数据不会使接收缓冲区溢出。该窗口时刻动态变化:当接收到来自对端的数据时,窗口大小减小,当接收端应用从缓冲区中读取数据时, 该窗口就增大。通告窗口大小减小到0是有可能的:当TCP对应某个套接字的接收缓冲区已满,导致它必须等待应用从该缓冲区读取数据时,方能从对端再接收数据。 TCP连接是全双工的,这意味着在一个给定的连接上,应用可以在任何时刻在进出两个方向上既发送数据又接收数据。 TCP必须为每个数据流方向跟踪诸如序列号和通告窗口大小等状态信息。建立一个全双工连接后,需要的话可以把他转为一个单双工连接。 UDP可以是全双工的。 TCP协议:
//4. TCP建立连接过程: A:服务器必须准备好接受外来的连接。这通常通过调用socket、bind、listen这3个函数来完成。称之为被动打开。 B:客户通过调用connect发起主动连接。这导致客户TCP发送一个SYN(同步)分节,它告诉服务器客户将在待建立的连接中发送的数据的初始序列号。 通常SYN分节不携带数据,其所在的IP报只含有一个IP首部、一个TCP首部及可能有的TCP选项 C:服务器必须确认(ACK)客户的SYN,同时也得发送一个SYN分节,含有服务器将在同一连接中发送的数据的初始序列号。服务器在单个分节中发送SYN和对客户SYN的ACK。 D:客户必须确认服务器的SYN。 以上交换至少需要三个分组,因此称为TCP的三路握手。 TCP的三路握手:
//5. 每一个SYN可以含有多个TCP选项,下面是一些常用的TCP选项: (1):MSS选项:发送SYN的TCP一端使用本选项通告对端他的最大分节大小,即MSS,也就是他在本连接的每个TCP分节中愿意接受的最大数据量(不包括2层协议头)大小。 TCP_MAXSEG 套接字选项可以提取与设置这个TCP选项 (2):窗口规模选项:TCP连接任何一端能够通告对端的最大窗口大小是65535(TCP协议里窗口大小占16bit),窗口规模选项指定TCP首部中窗口必须左移位数,取值范围:0-14 备注:由上可知,TCP窗口规模最大为65535*16384,即接近1GB,但是通过代码设置TCP接收和发送缓冲区大于1GB时,仍然成功,原因待考虑 种类 长度 名称 描述 0 1 EOL 选项列表结束 1 1 NOP 无操作,用于填充 2 4 MSS 最大段大小 3 3 WSOPT 窗口缩放因子 4 2 SACK_Permitted 发送者支持SACK选项 5 可变 SACK SACK阻塞(接收到乱序数据) 8 10 TSOPT 时间戳选项 28 4 UTO 用户超时 29 可变 TCP-AO 认证选项 253 可变 Experimental 保留 254 可变 Experimental 保留 //6. TCP连接终止: TCP建立一个连接需要三个分节,终止一个连接需要4个分节 A:某个应用进程首先调用close,我们称该端执行主动关闭。该端的TCP于是发送一个FIN分节,表示数据发送完毕 B:接收到这个FIN的对端执行被动关闭。这个FIN由TCP确认,他的接收作为文件结束符传递给接收端应用进程(放在以排队等候该应用程序接收的其他数据之后), FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收 C:一段时间后,接收到这个FIN的应用进程将调用close关闭他的套接字。这导致他的TCP也发送一个FIN D:接收这个最终FIN的原发送端确认这个FIN 以上,每个方向需要发送一个FIN和一个ACK,因此通常需要4个分节,但是有些情况步骤一的FIN会随数据一起发送,步骤二和步骤三也可能被合并发送。 在步骤二和步骤三之间,从执行被动关闭到执行主动关闭一端流动数据是有可能的,这称为半关闭 TCP连接关闭时的分组交换
//7. TCP为一个连接定义了11种状态,这些状态可由netstat显示 TCP状态转换图
TCP连接的分组交换
//8. TIME_WAIT状态: 停留在这个状态的持续时间是最大分节生命期(maximum segment lifetime, MSL)的两倍,称之为2MSL 任何TCP实现都必须为MSL选择一个值,RFC1122的建议是2分钟。 MSL是任何IP数据报能够在因特网上存活的最长时间 TIME_WAIT的存在理由: (1):可靠的实现TCP全双工连接的终止 (2):允许老的重复分节在网络中消逝 详解: 客户端发起的关闭时客户端和服务器的状态转换图:
具体过程如下: A:客户端发送FIN报文段,进入FIN_WAIT_1状态。 B:服务器端收到FIN报文段,发送ACK表示确认,进入CLOSE_WAIT状态。 C:客户端收到FIN的确认报文段,进入FIN_WAIT_2状态。 D:服务器端发送FIN报文端,进入LAST_ACK状态。 E:客户端收到FIN报文端,发送FIN的ACK,同时进入TIME_WAIT状态,启动TIME_WAIT定时器,超时时间设为2MSL。 F:服务器端收到FIN的ACK,进入CLOSED状态。 G:客户端在2MSL时间内没收到对端的任何响应,TIME_WAIT超时,进入CLOSED状态。 从上图中可以看出,client在发出server端FIN的ACK以后,进入了TIME_WAIT的状态,直到2MSL以后,才会关闭。 当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失。 这个规则导致一个后果就是在这个2MSL的时间内,该地址上的连接(客户端地址、端口和服务器端的地址、端口)不能被使用。 比如我们在建立一个链接后关闭链接然后迅速重启链接,那么就会出现端口不可用的情况。 TIME_WAIT状态的必要性: 客户端进入发送收到四次握手关闭的最后一个ACK后,进入TIME_WAIT同时发送ACK,如果其不停留2MSL时间,而是马上关闭连接,销毁连接上的资源,当发送如下情况时, 将不能正常的完成四次握手关闭: 客户端发送的ACK在网路上丢失,这样服务器端收不到最后的ACK,重传定时器超时,将重传FIN到客户端,由于客户端关于该连接的所有资源都释放,收到重传的FIN后, 它没有关于这个FIN的任何信息,所以向服务器端发送一个RST报文端,服务器端收到RST后,认为搞连接出现了异常(而非正常关闭)。 所以,在TIME_WAIT状态下等待2MSL时间端,是为了能够正确处理第一个ACK(最长生存时间为MSL)丢失的情况下,能够收到对端重传的FIN(最长生存时间为MSL),然后重传ACK。 是否只要主动关闭方在TIME_WAIT状态下停留2MSL,四次握手关闭就一定正常完成呢? 答案是否定的?可以考虑如下的情况,
TIME_WAIT状态下发送的ACK丢失,LAST_ACK时刻设定的重传定时器超时,发送重传的FIN,很不幸,这个FIN也丢失,主动关闭方在TIME_WAIT状态等待2MSL没收到任何报文段, 进入CLOSED状态,当此时被动关闭方并没有收到最后的ACK。所以即使要主动关闭方在TIME_WAIT状态下停留2MSL,也不一定表示四次握手关闭就一定正常完成。 结论:在TIME_WAIT下等待2MSL,只是为了尽最大努力保证四次握手正常关闭。确保老的报文段在网络中消失,不会影响新建立的连接 考虑如下的情况,主动关闭方在TIME_WAIT状态下发送的ACK由于网络延迟的原因没有按时到底(但并没有超过MSL的时间),导致被动关闭方重传FIN,在FIN重传后, 延迟的ACK到达,被动关闭方进入CLOSED状态,如果主动关闭方在TIME_WAIT状态下发送ACK后马上进入CLOSED状态(也就是没有等待)2MSL时间,则上述的连接已不存在: 现在考虑下面的情况,假设上述的链接的四元组为(192.201.0.80:23,192.201.0.85:5555),由于连接已关闭,我们可以马上建立一个新的连接, 并且这个连接的四元组也是(192.201.0.80:23,192.201.0.85:5555),那么当上一个连接的重传FIN到达主动关闭方时,被新的连接所接受,这将导致新的连接被复位,很显然,这不是我们希望看到的事情。 新的连接要建立,必须是在主动关闭方和被动关闭方都进入到CLOSED状态之后才有可能。所以,最有可能导致旧的报文段影响新的连接的情况是: 在TIME_WAIT状态之前,主动关闭方发送的报文端在网络中延迟,但是TIME_WAIT设定为2MSL时,这些报文端必然会在网络中消失(最大生存时间为MSL)。 被动关闭方最有可能影响新连接的报文段就是我们上面讨论的情况,对方ACK延迟到达,在此之前重传的FIN,这个报文端发送之后,TIME_WAIT的定时器超时时间肯定大于MSL, 在1MSL时间内,这个FIN要么在网络中因为生成时间到达而消失,要么到达主动关闭方被这确的处理,不会影响新建立的连接。 //9. 任何时候,多个进程可能同时使用TCP、UDP或者SCTP这三种传输层协议中的任何一种,这三种协议都使用16位整数来区分这些进程 IANA(the Internet Assigned Number Authority,因特网已分配数值权威机构)维护着一个端口号分配的清单,端口号被分为3段: (1):众所周知的端口:0-1023 这些端口由IANA分配和控制 (2):已登记的端口:1024-49151 这些端口不受IANA控制,不过由IANA登记并提供他们的使用情况的清单 (3):临时端口:49152-65535 IANA并不管这些端口 //10. 一个TCP连接的套接字对是定义该连接的两个端点的四元组:本地Ip,本地Port,外地Ip,外地Port 套接字对唯一标示一个网络上的每个TCP连接 当服务器监听一个Ip端口,多个客户端同时连接这个Ip端口。至此,我们必须在服务器主机上区分监听套接字和已连接套接字,这两种套接字都使用相同的端口,即服务器bind时候使用的端口 TCP无法仅仅通过查看目的端口号来分离外来的分节到不同的端点,他必须查看套接字的所有四个元素才能确定由哪个端点接收某个到达的分节 多个客户端与同一个服务器连接
//11. 影响IP数据报大小的限制: A:IPv4数据报的最大大小是65535字节,包括IPv4的首部,因为其总长度字段占据16位 B:IPv6数据报的最大大小是65575字节,包括40字节首部,这是因为其净荷长度字段占据16位。注意:其净荷长度字段不包括IPv6首部,而IPv4的总长度字段包括IPv4首部 IPv6有一个特大净荷选项,可以将净荷长度扩展为32位,不过这个选项需要MTU查过65535的数据链路提供支持(这是为主机到主机内部连接而设计的,比如HIPPI,他们通常没有内在的MTU) C:许多网络有一个可由硬件规定的MTU(maximum transmiss unit,最大传输单元)。以太网的MTU是1500字节 D:在两个主机之间的路径中最小的MTU称为路径MTU。两个主机之间相反方向上的MTU可以不一致,因为因特网中路由选择往往是不对称的。以太网的1500字节MTU是当今常见的路径MTU E:当一个IP数据报将从某个接口送出时,如果他的大小超过相应链路的MTU,IPv4和IPv6都将执行分片。这些片段在到达最终目的地前通常不会被重组 (备注:如何确保分片的数据的可靠性,待研究) IPv4主机对其产生的数据报执行分片,IPv4路由器则对其转发的数据报执行分片 IPv6只有主机对其产生的数据报进行分片,IPv6路由器(通常)不对其转发的数据报执行分片 F:IPv4首部的不分片(DF位)若被设置,那么不管是发送这些数据报的主机还是转发数据报的路由器,都不允许对他们进行分片,当路由器接收到一个超过其外出链路MTU大小且设置了DF位的IPv4报, 他将产生一个ICMPv4(目的地不可达,需分片但DF位已设置)出错消息 G:最小重组缓冲区大小是IPv4和IPv6的任何实现都必须支持的最小数据包大小,其值对于IPv4为576字节,对于IPv6为1500字节。 例如:就IPv4来说,我们不能判定某个给定的目的地能否接收577字节的数据报,为此使用UDP的IPv4网络应用(如DNS、RIP、TFTP、BOOTP、SNMP)应该避免产生大于这个大小的数据报 H:TCP的MSS(最大分节大小):用于向对端TCP通告对端在每个分节能发送的最大TCP数据量。MSS经常设置为MTU减去IP和TCP首部的固定长度,在以太网中使用IPv4的MSS值为1460,使用IPv6的MSS是1440 对具有特大净荷选项的IPv6,65535这个MSS值被视为无限的一个特殊值 MSS选项的目的之一就是避免分片 //12. 每一个TCP套接字都有一个发送缓冲区,可以使用 SO_SNDBUF 套接字选项来更改该缓冲区大小 当某个进程调用write时,内核从该应用进程的缓冲区中复制所有数据到所写套接字的发送缓冲区,如果该套接字的发送缓冲区容不下这么多数据,该进程会被投入睡眠(阻塞情况下) 从写一个TCP套接字的write调用成功,仅仅表示我们可以使用原来的应用进程缓冲区,并不表示对端的TCP已接收到数据 对端TCP必须确认收到的数据,伴随来自对端的ACK不断到达,本端TCP至此才能从套接字发送缓冲区丢弃已确认的数据,TCP必须为已发送的数据保留一个副本,直到他被对端确认为止 进程写TCP套接字时涉及的缓冲区和步骤
//13. 任何UDP套接字都有发送缓冲区大小,可以使用 SO_SNDBUF 套接字选项去更改他,不过其对应的发送缓冲区实际上并不存在,这个发送缓冲区大小仅仅是可以写到该套接字的UDP数据报的大小上限 如果进程写一个大于套接字发送缓冲区大小的数据报,内核将返回一个 EMSGSIZE 错误。既然UDP是不可靠的,他就不必保存进程数据的副本,因此无需一个真正的发送缓冲区 应用进程的发送的数据在沿协议栈向下传递时,通常被复制到某种格式的一个内核缓冲区,然而当该数据被发送后,这个副本就被数据链路层丢弃了 从写一个UDP套接字的write调用成功返回表示所写的数据报或其片段已被加入数据链路层的输出队列。如果该队列没有足够的空间存放该数据报或其某个片段,内核通常返回一个 ENOBUFS 错误 注意:有些UDP的实现不返回这种错误,这样甚至数据报未经发送就被丢弃的情况,进程也是不知道的 进程写UDP套接字时涉及的缓冲区与步骤
时间: 2024-10-13 10:21:25