UNP_Chapter02_TransportLayer_笔记总结

            ***PAY A TRIBUTE TO W.Richard Stevens***

Chapter01: 传输层协议: TCP, UDP, SCTP

2.1 简介

在这章我们主要探讨TCP/IP族中的协议,从编程者的角度去分析和理解如何去使用这些协议并且提供更为详细的描述.
这一张我们主要集中在传输层协议上: TCP, UDP和流控制传输协议(SCTP). 大部分的CS应用都是使用TCP和UDP. SCTP是新出来的一种传出层协议主要负责进行互联网中电话信号的传输.这些传输层的协议用的就是网络层的IP协议,也就是Ipv4或者Ipv6. 但是也是能够绕过传输层只进行IP层的使用,这就是传说中的raw socket,但是很少用了.
UDP是一种简单的不可靠的数据段协议,但是TCP是一种复杂的可靠的字节流协议. SCTP也是可靠的传输层协议,但是它是有信息界限的. 我们需要知道这些传输层协议是怎么应用的应用程序中的,所以我们需要知道如何去把握这些协议.
我们只有熟悉了TCP的特征,才能写出健壮的客户端和服务器端. 同样的,也只有知道这些特征,我们才能很好的debug客户端和服务器端用牛逼工具netstat. 在这章中我们会涉及: TCP的三次握手, TCP的连接终止序列, TCP的TIME_WAIT状态, SCTP的四次握手, 以及SCTP的链接终止, 还会涉及到SCTP, TCP和UDP的socket层的缓冲等.

2.2 宏图

这里写图片描述
大家不要被协议族的名字所迷惑: TCP/IP, 其实这里的协议远远超过TCP和IP协议. 我们看最顶端的应用层,从最右面到最左面,最右面的五个应用工具是使用的IPv6, 左面的工具使用的是IPv4. 这个tcpdump是相当重要的分析工具, 它使用BSD包过滤器(BPF)或者数据链路接口(DLPI)直接与数据链路层进行了交互. 在这幅图的九个应用程序下面可以看到有一条虚线标记为了API, 这就是传说中的socket或者XTI. 当然BPF和DLPI就不用socket进行交互了.
从图中也能看出,traceroute是使用两个socket进行交互,一个是IP, 另一个是ICMP.
IPv4和IPv6是什么就不啰嗦了.
TCP: 是一种面向连接的可靠协议, 全双工字节流协议. TCPsocket就是stream socket. TCP关系ack(Acknowledgment),超时,重传等. 大部分的应用程序都会使用TCP, 并且TCP是支持IPv4和IPv6的.
UDP: User datagram protocl, 无连接协议,它的socket就是datagram socket. 对是否能完整或成功到达目的地是没有保证的. 同样也能支持IPv4和IPv6.
SCTP: 流控制传输协议. 是一种面向连接的全双工可靠协议. 它是多宿主的,也就是在连接双方可以有多个IP对应一个端口进行通讯. SCTP不进支持IPv4和IPv6, 并且可能在同一个连接中能支持它们一起.
ICMP:网络消息控制协议. ICMP在主机和路由器之间处理错误和控制信息, 这些信息通常都是由TCP/IP网络软件自己生成的, 不是用户程序, 尽管我们用ping, traceroute等程序. 我们也会指定ICMPv4来区分ICMPv6.
IGMP: 网络组管理协议. 这是个多路协议,在IPv4中是可以选择的.
ARP: 地址解析协议. ARP将一个IPv4地址映射成一个硬件地址(比如说以太网地址), ARP一般用于以太网,令牌环网(token ring), 和FDDI. 在p2p网络中是用不到的.
RARP: 地址泛解析协议. RARP协议将硬件地址转换成IPv4地址. 又是用于无盘节点的引导.那什么是无盘节点的引导呢? 一台无盘机器是一台没有任何诸如硬盘、软盘驱动器或CD-ROM等常见引导设备的PC。无盘节点引导自网络,并需要一台服务器来提供当作本地硬盘来使用的存储空间。从现在开始,服务器将称为“master”,而无盘机器将称为“slave”(从名字中能看出来什么?:)。slave节点需要一块支持PXE引导或Etherboot的网络适配器。现在大多数的卡都支持PXE,很多主机集成的适配器也同样可以。diskless node booting配置大家可以看这篇配置文章来对PXE引导有深刻的理解.
ICMPv6综合了ICMPv4, IGMP和ARP的功能.
***BPF***BSD包过滤协议. 这个接口提供了数据链路层的通路. 这个协议一般在BSD引导的内核中.
DLPI: 数据链路提供接口. 这个接口也是数据链路层的通路. 通常由SVR4内核的系统提供.
这些不同的协议都有一个甚至多个RFC(Request for Comments)文件提供正式的说明支持. 我们通常称同时支持IPv4, IPv6的主机叫IPv4/IPv6主机或者双栈主机. 在4.4BSD中实现了的TCP/IP在TCPv2中详细定义.

2.3 UDP

UDP是一种简单的传输层的协议. 具体的RFC定义是RFC768. 应用层将数据将数据写入到socket中,这个socket将会进行封装(encapsulated)成UDP数据段,然后再传递到网络层,封装成IP数据段,最后送往目的地. 在整个过程中,并不能保证每次都成功到达终点,也不能保证到达后的顺序正确,也不能保证保证UDP数据段只发送一次.
在我们的UDP编程中涉及到的问题就是很不可靠. 如果数据段到达了目的地但是checksum检测到错误,或者数据段在网络中被扔掉了,都不会传送到目的端的socket中,并且源端也不会自动的重传.但是如果我们想知道到底数据段传送到没时,我们就可以在我们的应用程序中加入比如ack, 超时检测,重传等机制.
每个UDP都会将发送端写的数据长度都随着数据一起传到接收端的应用程序. 我们之前说到TCP是流协议,没有边界,但是UDP是有边界的.
我们也知道UDP是面向无连接的, 也就说它并不需要和服务端保持持久的连接. 比如说一个客户端给服务端发送了一个数据段后下一个数据段有可能通过相同的socket传输给了另外一个server. 当然服务端也可能是收到很多数据段信息,但是没条都是来自于不同的客户端.

2.4 TCP

TCP提供的服务是不同于UDP的. TCP定义在RFC793. 更新版本是RFC1323, RFC2581, RFC2988和RFC3390. 从这里也能看出TCP是更为复杂的一种协议. TCP是面向连接的协议,当于服务器端建立起连接后,会进行信息交互后才断开.
并且TCP也是一种可靠的传输协议. 每次向服务端发送数据后都会等待ACK的返回进行确认,如果ACK没有收到,那么TCP在等待一段时间后会选择重传. 在差不多4-10(依据不同实现而不同)分钟的不断重传还没到达后就会放弃.
虽然TCP是可靠协议,但是也不是100%可靠的,因为TCP是没法保证数据肯定被另一端收到的,它只是用这些重传等机制来提高准确性.
TCP使用算法动态的估算RTT(round-trip time)在服务端和客户端的传输中. 通过RTT, 就能知道等待一个ACK需要多久. 因为不同的网络环境中的RTT会不同. 比如说在LAN中,RTT应该就是毫秒级的但是在WAN中就是秒级的. TCP会在指定的连接中持续的估算RTT.
TCP会为传输的每个字节关联一个序列号,这样是为了排序. 比如说, 假设应用程序往TCP socket中写了2048字节的数据, TCP就会发送两个分节(segments),分节就是TCP的传输单元,UDP是数据段(datagram). 序号分别是1-1024和1025-2048. 如果到达传输层是乱序了的,那么接收端就会通过这些序列号进行排序然后再传输给应用程序. 通过这个序列号还能进行去重,因为如果TCP因为网络负载或延迟等原因误以为丢失了而重发了分节,但是在接收端会根据序列号将后到的扔掉.
TCP还有流量控制(flow control)的功能.TCP能总是告诉对等方一次最多能接受多大的数据.这就是通告窗口(advertised window). 在任一时刻,接收窗口就是接收buffer当前的可用容量, 这样就保证了发送端不会在接收buffer中溢出. 这个windows大小是动态变化的,如果发送端发送了数据到接收buffer,那么这个size就会降低,当应用程序读取了数据,这个size就会增大. 当这个buffer满了,size就成了0, 那么收端就不会再接收对等端发送过来的数据而是等待应用层读取数据.
TCP也是全双工的(full-duplex). 也就是发送端也会是接收端. 这就是说TCP必须要维护好发送和接收的sequence数, window大小为两端. 但是当建立一个全双工连接后可以改变成单工. UDP也可以使全双工.

2.5 SCTP

Stream Control Transmission Protocol. SCPT提供的服务类似于UDP和TCP提供的服务. SCTP在RFC2960中定义,更新在RFC3309, SCTP的简介在RFC3286中. SCTP在客户端和服务器端建立了关联(association). SCTP也给应用层提供了可靠的,序列号,流量控制全双工等类似于TCP的服务. 这个关联这个词取代之前我们提到的连接(connection)是为了避免一个隐含之意(connotation):一个连接(connection)表示的仅仅是IP地址之间的连接. 关联(association)是可以在两个系统之间建立连接的,也就是说在两个宿主之间可能是多个IP互连.
与TCP不同的是SCTP是消息导向的. 它提供各个记录的按序递送服务. 和UDP类似,由发送端写入的每条记录的长度随数据一同传给接收端应用.
SCTP能够在链接的双端提供多个流, 每个流各自可靠的按序传输. 当在一个流上信息丢失时并不会影响到其他流的信息传输,这一点不同于TCP, 在一个流中一旦某个信息传输出错,就会阻塞后面的直到这个修复.
SCTP还提供了多宿主的特点,是的单个的SCTP端点能够支持多个IP地址. 这个特点能够增强网络中故障的健壮性. 一个端点可以有多个冗余的网络连接, 每个网络又可能有各自接入到因特网的基础设施连接. 当该端点和另一个端点建立连接后,如果某个网络或者跨越因特网的通路发生故障,SCTP就能够切换到和这个连接的另一个端点进行连接.
类似的健壮性在路由协议的支持下也是能用TCP实现的. 比如BGP是一中路由协议. iBGP表示在一个域内的BGP连接, 这是路由器之间的协议. iBGP就经常用两个路由器的虚拟地址进行TCP的连接,这样只要路由器之间有路由连接就能保证健壮性,但是如果不用虚拟地址而用成了路由器的物理地址,那么就无法保证健壮性了,一旦断开就是gg的节奏. SCTP不进适用在路由器间,它是支持多个主机的, 并且允许不同的宿主连接到不同的服务提供商. 这是TCP结合路由办不到的.

2.6 TCP连接的建立和终止

通过学习这一节能够使我们更清楚的使用connect, accept和close等函数,并且当我们使用netstat进行检测的时候也能进行很好的拍错,所以这一节很重要,也是面试中出现频率极高的.
我们先说建立连接的三次握手:
1. 服务器端必须准备好接收即将到来的连接. 通常调用socket, bind和Listen函数. 这也就是常说的passive open.
2. 客户端发布一个active open通过调用函数connect, 这使得客户端的TCP发送了一个同步分节(synchronize segment) SYN, 这就是告诉了服务器我客户端在连接这个路上发送的初始化数据的序列号.同常来说是没有应用层的数据在这个SYN中包含着的, 一般就是IP头, TCP头和可能的可选TCP.
3. 当服务器端收到后,必须返回acknowledge(ACK)来承认客户端的SYN收到并且服务器端也要返回自己SYN其中包含了这个数据的序列号. 也就是说服务器发送了ACK和SYN.
4. 客户端发送ACK就可以了.
这里写图片描述
这就是闻名已久的三次握手.注意序列号的值.
建立TCP连接就和我们的打电话一样一样的,我们来类比一下:
socket函数就好比是有电话可用, bind函数就是告诉别人你的电话号码, 这样他们就能呼叫你. listen就是打开函数的铃声,这样当有一个外来的电话到来时,就可以听到. connect函数就是让我们输入某个人的电话号码并且拨打. accept发生在被呼叫的人应答电话之时. 由accept返回客户的标识(客户的IP和端口号) 类似于让电话机的呼叫者ID功能部件显示呼叫者的信息. 然后不同的是,accept只有在我们接起电话的时候才能显示呼叫着的ID, 但是我们电话显示功能在我们没有应答的时候也会显示出来. 如果使用DNS, 它就提供一种类似电话薄的功能, getaddrinfo就类似于在电话薄中查找某个人的信息, getnameinfo就类似于有一本按照电话号码而不是名字排序的电话薄.
每个SYN都是可以包含TCP选项的:
·MSS选项. 拥有了这个选项,发送SYN的TCP端就会告知对等端它的最大分节的大小(maximum segment size)MSS, 也就是在本连接中它愿意接受的每个分节的最大字节. 所以说接收方也要根据发送方的MSS来调整它即将发出的分节大小.这个选项是TCP_MAXSEG选项.
·窗口大小选项. TCP能够通告对方的最大窗口大小是65535, 因为在TCP的头部的相应位置只有16bits. 但是当今的网络已普及高速网络或者是长延迟路径(卫星链路)要求更大的窗口来尽可能的实现最大吞吐量. 这个选项就要求TCP头部能够扩大window size, 也就是左移0-14位.这样window size能够达到G级别(65535*2^14). 双方终端系统都要支持这个选项. 这个变量是用SO_RCVBUF定义的.
为实现与早期没有实现这个选项的系统的互操作. TCP可以作为主动打开的部分内容随它的SYN发送这个选项信息. 但是只有在对端也随它的SYN发送这个选项的时候,才能进行扩大window size. 类似的,服务器端只有接收到客户端发过来的带有option的SYN才能进行发送这个选项.
·时间戳选项. 这个选项就是为了防止在高速网络下防止数据被旧的,延迟了的重复了的数据而破坏了,这是个新的选项.
相对于连接时的三次握手,关闭时需要进行四次握手:
1. 首先是应用层调用close方法, 我们成这一端执行主动关闭. 这一端的TCP发送了一个FIN分节. 就是意味着结束传输数据.
2. 接收到FIN的另一端就是消极关闭端. 收到FIN后,会将这个FIN分节以end-of-file的数据形式放在内核buffer的队列末尾,也是要同样传输给应用层的. 因为FIN在接收端表示的就是不会再有数据传输上去了.
3. 一段时间后,应用程序读到了end-of-file后,就会关闭他的socket. 这也就造成TCP发送了一个FIN分节给active close端. (再发送FIN之前已经将收到的FIN返回了一个ACK)
4. active close端确认收到FIN.
通常情况下因为双方都是需要进行ACK确认的, 所以说传输了四个分节,但是也是有特殊情况的,比如第一次传输的FIN有可能携带有数据,第二次分开传输的ACK和FIN又可能合在一起进行传输.
FIN和SYN一样,都是占用了一个字节的序列号空间. 因此每个ACK都是收到的FIN+1.
我们看看这个TCP报文头:
这里写图片描述
顺便看一下close过程图:
这里写图片描述
在从passive close端向active close端发送FIN和ACK的这个阶段可能是发送数据的. 这就是我们常说的半关闭状态. 我们会面会详解这个shutdown函数.
双方能发送FIN是因为应用层调用了close函数,但是在现实中,当一个中断过来,或者调动了exit,或者main函数return了等将进程结束的方式都会使得所有打开的文件描述符关闭,也就会想所有连接这的TCP另一端发送FIN.
一般情况下都是客户端充当active close的,但是在HTTP/1.0中是server端的active close.
这里我们呈上一个很重要的TCP的11种状态机的图:
这里写图片描述
我们还有些情况在这个图中没有显示出来,一种是同时发送SYN,另一种是同时发送FIN的. 当我们用netstat检测服务器端和客户端的状态的时候,这些状态机都会显示出来.
我们来一张更加详细加上方法和状态机的图:
这里写图片描述
交互双方的MSS不同时可以的. 一旦建立起连接, C行程了requst给S. 如果我们假设一次请求数据大小正好小于1460,然后服务器端收到请求然后一般都能在200ms之内处理发送出(加入这次相应和ACK也是下于536)reply和对客户端request的ACK.(注意他们是在一个segment中). 这就是叫做piggybacking. 中文意思就是说扛着传输. 当piggybacking时间过于长的时候,比如1s, 客户端总是会先收到确认然后随后跟着reply数据. 我们稍后讨论主动关闭方的最后一个状态TIME_WAIT状态.
可以看出单一发送一个请求和收到一个相应就需要这么复杂,花费8个分节. 如果用UDP的话就是简单的两个数据段. 但是UDP就会将TCP的好多机制都扔掉, TCP提供的一个很重要的机制就是拥塞控制,这个必须由UDP应用层来控制.在网络应用中好多应用程序是用UDP写的,因为它们传输不需要消耗太大,并且速度快.

2.7 TIME_WAIT

这个是一个很重要的状态,在你写CS服务器的时候肯定会看到,这也是我在面试的时候碰见的一个问题,当时我就没答上来,重视起来~.
当我们都正常关闭后,主动方会进入到TIME_WAIT状态持续2MSL(Maximum segment lifetime).
TCP的每个实现都是需要选择一个MSL值的. 在RFC1122中建议是2分钟,但是在BSD网络系统中是30s, 也就是说MSL一般就是在1~4分钟不等. MSL就是说IP数据段在网络中能存活的最大时间,也就是说超过这个时候就死了. 我们经常听到网络总的词汇是跳数, 也就是TTL, 每经过一个路由器TTL+1,如果到255都没打到指定目的地,那么路由器就会扔掉. 这个还并不是真正意义上的时间. 真正的时间就是MSL.
一个包的丢失经常就是路由器的发生故障的问题, 比如说路由器崩了或者是两个路由器之间的链路速率下降有了很长的延迟, 路由环(a给b, b又给a)等这些情况,都需要路由协议耗费几秒甚至几十秒来寻找另外一条可行的路线. 那么当主动关闭方发送了ack后,由于上面提到情况,那么超时后,服务器没收到ack,服务器就又发送了个FIN,现在网络好了,也就是说两个ack都到了,但是旧的ack是不能要的,如果刚收到新的ack后服务器知道能关了,这时又是同一个IP同一个端口建立了链接,此时之前遗留在网络中的ack也来了,这就混乱了,所以为了避免错误,就保持那个状态2MSL, 这样在网络中的ack绝壁跪了. 这样设置还有一个原因就是主动关闭方必须保持TIME_WAIT状态,这样才能收到服务端重新发的FIN,重新发送ACK, 如果不维持这个状态它就会给服务端回应RST标志位. 服务器端会将这个标志位全部当做是err异常抛出.
那么为什么BSD设置30秒都没问题呢?是因为BSD是根据检测新建立的连接发送的SYN的序列号是否比之前的大这种方式.

2.8 SCTP的association

SCTP的四次握手:
这里写图片描述
1. S端必须时刻准备着进来的association. 和TCP同样的会调用socket, bind和listen函数.
2. C通过调用connect或者是发送一个信息来隐式的打开association. 这一步造就了C的SCTP发送了一个INIT的信息来告诉服务器端客户端的IP列表, 初始化序列号,用来识别在这次association中的所有包的标签, C请求的外出流的数目,和客户能够支撑的进入流的数目.
3. 服务器以返回INIT-ACK确认收到客户端的INIT. 其中包含了: 服务器端的IP列表, 初始化序列号,初始化标签,服务端请求的外出流数目,服务端能支持的进入流的数目还有一个状态缓存. 状态cookie包含了确认本次关联正常的所有所需的状态,它是数字化签名过的,以确保其有效性.
4. C以一个COOKIE-ECHO作为回应S的cookie, 这条消息可能在同一包中还捆绑了用户信息.
5. 服务器以一个COOKIE-ACK进行确认用户回射的cookie是正确的, 该消息也可能在同一个包中捆绑了用户消息,就此关联建立.
因为SCTP建立连接最少要发送四个分节,所以我们称这个过程为SCTP的四次握手. 在这个过程中,首先是发送了INIT,在这个表中携带有一个验证标签Ta和一个序列号j. Ta必须在整个SCTP关联的生命周期中在对等方发出的每个包中都得出现,也就是会伴随着整个连接状态中的对等方发送包. J就是用户数据的块儿的其实序列. 同样的,在INIT的接收端要发送一个INIT-ACK, 这个包含着它创建的验证标签Tz, 还有序列K, 还有Ta标签,还有cookie C. 这个C中包含着客户端与服务器建立连接所有的状态信息,这样的话,在服务器端的栈中就不需要维护这些连接状态信息了. 下次客户端发送COOKIE-ECHO和Tz标签,最后服务端发送COOKIE-ACK携带Ta标签.
SCTP的双方必须选定一个主要目的地址,当网络中没有出现故障的时候,这个地址就是数据传输的默认目的地. SCTP中维护的这个cookie也是防止DoS攻击的有效手段(denial-of-service). 我们在后面也会讨论的. TCP也效仿这个进行DoS防止, 但是TCP必须将cookie的数据压倒sequence 中,因为没有给它多余的空间放这个,所以就只能有32bits. 但是SCTP有专门的地方放cookie并且进行了加密.
下面我们讨论一下SCTP的关闭过程:
这里写图片描述
首先说明SCTP中并不支持类似于TCP的半关闭情况,只要有一段发起shutdown请求,那么对等方就不会再给它的对等方发送用户数据. 当收到SHUTDOWM请求后,会把queue中的数据送到用户层处理一下,如果没了,那么就发送SHUTDONW-ACK, 最后主动方发送SHUTDOWN-COMPLETE就直接结束了. 这里并不存在像TCP一样的TIME_WAIT状态,因为它在每次的传输中都维护这Ta和Tz,这样的话,有old包来的时候就能验证出标签不对.
那么它的状态是什么样的呢?
这里写图片描述
我们能看到在四次握手中客户端发送COOKIE-ECHO的时候顺便带上了发送的数据块,在服务器端回应的COOKIE-ACK中也是带上了回应的数据块儿. 一般情况下COOKIE-ECHO携带的数据是一个或多个当应用程序调用一对多的接口时. 在每个数据块儿中有块儿大小,类型,flags, 这样以块的形式,也是很方便与在传输中多个的绑定.
SCTP中参数和块这两个特点促进了SCTP可选特点的发展. SCTP可以汇报未知的参数和未知的块儿,在参数和块儿的最高两位就是告诉SCTP接收端如何去处理未知的情况. 现在SCTP主要的研究方向是:
1. 动态对地址进行扩展,也就是允许相互合作中的SCTP在终端能动态删除或者增加IP.
2. 还有一种就是非完全可靠的扩展,在端点的应用层底下对数据的重传进行限制,就是说如果这个数据太老了不需要重传了的时候,就会直接跳过这个数据的重新传输, 这也就造就了一些不可靠因素.

2.9 端口号

我们所说的应用程序其实每个应用程序就是一个进程,那么多个进程访问服务器的进程时,服务器是怎么识别访问的是哪个进程呢?那么针对不同的进程定义了一系列的端口号,针对某些特殊的应用程序(进程)有着固定的端口号.比如,FTP这个进程就给所有TCP/IP实现分配了21, TFTP针对传输层的UDP实现分配了69这个端口号. 当然当服务器传回数据的时候也是需要给到客户端的应用进程的,也是要通过端口号的,那么这些端口号就是临时的端口号,并且是传输层自动分配的,其实在client请求服务器的时候就分配了,客户端应用进程是不需要关心自己的这个端口的,这是经过传输层的时候自动分配的.
IANA(Internet Assigned Numbers Authority)就是维护端口号列表的. 端口号主要分成以下三类:
1. 0到1023进行知名端口号的定义这些由IANA进行分配管理, 我们总会听到TCP的端口号是多少多少,UDP是多少,其实端口号是应用层和传输层链接的口, 所以当我们定义Web服务器是80端口的时候,那么它所用到的传输层协议即TCP和UDP也就是80了. 但是在80分配的时候SCTP并没有出现,但是新的规定TCP所支持的所有端口号在SCTP上都能够使用.
2. 1024到49151. 这些端口并不在IANA管辖, 但是IANA等级并且列出了它们的使用情况. 比如说6000到6063就分配在了X Window服务器上. 同时也是为TCP和UDP分配了的.
3. 49152到65535, IANA完全没有理会这些端口号,这也就是临时端口的使用.
但是以上针对不同系统又有着不同的定义:
这里写图片描述
我们可以看出,在Unix系统中有着保留端口号的概念, 这些端口号和IANA管理分配的一样, 这些端口号针对不同的应用进程要修改的时候只能root权限去更改.
在早期的4.3BSD中定义1024到5000是临时端口号,但是我们现在并发进程何止几千,所以现在系统实现都是使用IANA定义的标准,使用后面的为临时端口号.
在某些情况下客户端也会需要reserved端口的作为客户端和服务器端的认证. 比如说rlogin和rsh客户端.这些客户端调用rresvport(3)函数创建TCP socket 并且分配一个513-1023未用的端口号(从后往前分分配). 那么什么是rsh呢? Rsh 是远程外壳(remote shell) 的缩写(外壳是操作系统的一种命令接口)。运行于远程计算机上的rshd 后台程序,接受rsh 命令,验证用户名和主机名信息,并执行该命令。当用户不愿或不需要与远程计算机建立远程会话时,可以使用rsh 工具执行输入的命令。Rsh 工具允许用户在远程计算机上执行单条命令,而无需在该远程计算机上进行登录. 那么什么是rlogin? 远程登录(rlogin)是一个 UNIX 命令,它允许授权用户进入网络中的其它 UNIX 机器并且就像用户在现场操作一样。一旦进入主机,用户可以操作主机允许的任何事情,比如:读文件、编辑文件或删除文件等。Rlogin:远程登录命令 rlogin:Remote Login in Unix systems. 我们看看官方对rresvport的定义:
这里写图片描述
我们肯定都见过socket pair,这对于TCP来说是四元组: 本地IP, 本地端口,远端IP,远端端口号. 但是对于SCTP来说,如果两端都不是多宿主的情况下,和TCP是一样的,但是如果只要有一端是多宿主,那么有可能就包含多个四元组了,虽然本地IP很多,但是本地端口是统一的,虽然远端IP很多,远端端口也是统一的.

2.10 TCP端口号和多并发服务器

接下来我们引进一个多并发服务器器是如何识别不同访问的以及简单的是如何处理请求的:(我们现在的多并发服务器不会使用这种方式处理,因为用进程处理太浪费了,比如现在流行的高并发HTTP服务器Nginx用到就是epoll技术).:
这里写图片描述
加入说我们现在在freeBSD上有个服务器,它是多宿主的,也就是说有多个IP,或者是多个本地(网络)接口(local interface). 我们多宿主服务器的IP是12.106.32.254和192.168.42.1, 然后我们分析一下{*:21, *:*}的意思. ,前面的一对是我们的本地的接口和针对等待接收客户端请求的服务器应用的端口号, 后面一对表示的是访问过来的客户端的IP地址和客户端的端口号,这两这样写表示的就是说我对我服务器本地的所有接口都开放(也就是所有网卡都等待),这就像我们之前写的一个程序在地址结构体中指定INADDR_ANY. 然后我服务端应用程序用到的是21端口号,我接收任何客户端的IP地址和任何端口号. 这个通配符(wildcard)类型在我们做网络监控的时候netstat会看到的.
这里写图片描述
我们客户端206.168.112.219端口是1500的应用进程与12.106.32.254端口是21的服务器进程进行了连接请求connect. 这个查看客户端的netstat会发现的. 这幅图中的server端还是处于listen的socket阶段,因为还没有真正的建立连接,只是将连接请求放在了listen队列中了.
这里写图片描述
在这个图中我们会发现建立了链接,并且服务器进程是通过创建了一个子进程而处理的这个请求. 当它们建立了真正的链接后就是connect socket了. 然后服务器端的网络监测的时候就会发现填满了.当我们在客户端又有一个请求时,它们的进程不一样所以又不一样的端口号:
这里写图片描述
服务器主进程监听轮训到时识别到虽然IP相同但是不同的客户端进程,所以会再创建出一个子进程单独去处理这个请求.
这就是简单的多并发模型,只不过现在没有服务器这样用.

2.11 buffer大小和限制

这里我们引入一些限制IP数据报(datagram)的大小设置限制:
1. IPv4数据报的带下是65536字节,其中包括IPv4的头. 这是因为在IPv4的长度字段中只有16bit.
2. IPv6数据报的最大大小是65575字节, 包括40字节的IPv6头部. 这是因为payload(净荷)段只有16bit,那不是65535字节吗?说明IPv6的这个长度域中能显示的大小是不包含头部的. IPv6有一个超大净荷选项, 这个选项可以将净荷域的长度拓展到32bit, 不过这个选项需要MTU(Maximum transmission unit)超过65535的数据链路层支持.
3. 许多网络有一个可由硬件支持的MTU. 比如说以太网的MTU是1500字节. 另外有些链路协议如(PPP点对点链路)其MTU能够自己配置. IPv4要求的最小链路MTU是68字节. 这就允许最大的IPv4头部(20子节的固定头部和30字节的可选部分)拼接最小的片段(IPv4首部中片段偏移以8个字节为单位).IPv6要求的最小链路MTU是1280字节,IPv6可以运行在比这个值还小的链路上,不过需要特定于链路的切片和分组功能,以是的这些链路看起来至少为1280子节的MTU.
4. 两个主机路径之间的最小MTU就叫做path MTU. 如今以太网的MTU是1500字节,就是路径MTU. 路径MTU没有必须在两个主机之间相同,因为路由往往就不是对称的. 也即是路由器A到B和B到A的路径MTU可以不同.
5. 当IP数据报从接口上传送出去后如果超过了链路MTU,那么IPv4和IPv6的分片机制就用上了. 这些分片在到达目的地之前一般不会重组的. IPv4主机对其产生的数据报进行分片, IPv4路由器则对其转发的数据报进行分片. (注意路由器也是一个主机,里面也是unix系统). (IP与数据链路层之间就是所谓的接口层. )但是IPv6值有IPv6主机会进行分片,IPv6路由器是不会对要转发的IPv6数据报分片的. 这里注意,如果是路由器自己产生的的IPv6数据报,路由器是会分片的,只是不会分片来自其他主机的,自己只是做个转发而已. 比如说网络管理员能够配置路由器支持Telnet协议,那么IPv6就可能由路由器的telnet服务器功能.
你可能发现在IPv4头部有处理分片的字段,但是在IPv6中没有这样单独的字段,这是因为分片是例外情况而不是规则部分,在IPv6的可选字段中有分片的选项.
一些通常用作路由器的防火墙可能重组分组了的分片,以便查看整个IP数据包的内容,这样做使得防火墙上不必引入额外的复杂性能就能防止某些攻击,还要求防火墙设备是进出网络唯一路径上的设备.
6. 如果在IPv4的分片设置域中设置了DF标志位(don’t fragement), 表示不管是主机还是路由器都别给我分片这个数据报,但是如果超过了MTU, 那么就返回ICMPv4告诉不可达,要分片而不是设置成DF的错误信息.
如果是IPv6, 因为路由器不执行转发报文的分片,所以其实内部隐士的有个DF位,当IPv6路由器检测到超了链路上的MTU, 就会生成ICMPv6,说包太大的错误.
IPv4的DF位和IPv6的隐藏DF位可用于path MTU的发现, 例如,如果基于IPv4的TCP使用该技术, 那么它将在所发送的所有数据报中设置DF位, 如果某个路径返回ICMP错误信息, TCP就减少每个数据报的数量并重新传. path MTU发现对于IPv4是可选的, 然后IPv6的所有实现要么支持它,要么必须使用最小的MTU发送数据报. 但是当今的路由器好多是不给你返回ICMP信息的,所以使用TCP来进行path MTU发现是有问题的,IETF正在努力发现不依赖ICMP错误信息而进行path MTU发现.
7. IPv4和IPv6都定义了最小重组buffer大小,它是IPv4和IPv6任何实现都支持的最小数据报大小. 这个值对于IPv4是576字节,对于IPv6是1500字节. 对于IPv4来说我们并不能保证每个主机都支持大于577字节的数据报,所以好多基于IPv4的应用DNS, RIP, TFTP, BOOTP, SNMP设置防止超过576字节的数据报的产生.
8. 对于TCP来说有个MSS(maximum segment size), 这个就是在SYN的数据中携带着告诉对方你给我发的时候我能接收的最大segment就是这么大. 这主要就是为了告诉对等方我这里的最大重组buffer是多大, 也是为了避免分片的发生. 这个MSS的值就是接口MTU减去IP固定大小和TCP头部,所以对应以太网上IPv4是1460(1500-20-20), IPv6是1440(1500-20-40).TCP头都是20.
9. 在TCP的MSS字段中有16bits,所以最大值也是65535,这对于IPv4来说是可以的,因为IPv4的数据报中的最大TCP segment是65535-20-20, 然后对于具有超大净荷的IPv6来说就得用其他的技术了, 65535只有在设置了最大净荷的时候才使用这个值,否则就是65535-20, 但是实际的MTU是超过65535的, 那么TCP使用最大净荷选项的话,那么它所发送数据的大小限制就是接口的MTU.
10. SCTP基于对端发现的所有地址的最小path MTU保持一个分片点,这个最小MTU用于把较大的用户信息分割成较小的能够以单个IP数据报发送的若干片段. SCTP_MAXSEG socket选项可以影响这个值, 允许用户请求一个最小的分片点.
上面叙述了这么多原理,那么我们现在用一个图来分析我们发送数据的过程以及其中涉及到的buffer情况(重点):
这里写图片描述
每个TCP都有发送缓冲区,我们可以通过SO_SNDBUF来进行其大小的设定, 当应用层调用write函数的时候,内核将应用层buffer中的数据全部拷贝到socket send buffer中. 如果socket send buffer满了或者不够一次性拷贝来自应用层buffer的数据,那么进程就会睡眠,这里我们用到的是阻塞socket,我们以后会介绍到非阻塞socket. kernel将不会返回给用户层直到将应用层缓冲区中的全部数据全部拷贝到socket send buffer中,所以说我们应用层如果收到write的返回,并不是说明数据传送到了服务端了.
TCP从socket send buffer中取到数据然后依据TCP的传输规则将数据传输到服务端. 服务端必须确认收到,我们客户端之后收到ACK后才会将socket send buffer中存放的重发的数据丢弃掉.TCP必须保持一个数据的拷贝直到收到相应的ACK.
TCP发送数据到IP层用MSS大小或者是更小的块儿,注意这个MSS是没有包含TCP头的, 然后加上为每个segment加上TCP的头,这个MSS就是通过SYN中的MSS值确定的,如果没有指定的会话就是默认的536. 然后IP再加上它自己的头,通过检索路由表找到目的地址对应的接口,然后将datagram传输到合适的数据链路上. IP可能在传输到数据链路的时候切片,但是我们之前说过了,MSS的目的就是为了避免这一点和实现最小MTU discovery. 每个数据链路都是一个输出queue, 如果这个queue满了,包就会被丢掉,然后返回错误信息给IP然后给了TCP,然后TCP就会安排重传,我们用户层是完全不会知道的.
我们看完了TCP的输出情况,我们再看看UDP的输出情况.
这里写图片描述
应用进程调用sendto将用户进程buffer中的数据写入到内核的”socket send buffer”中,这里的内核buffer其实不存在的,但是有个宏SO_SNDBUF这个可以改,看见是buf的大小,其实这个只是表示从用户进程拷贝到内核的最大上限. 如果用户进程写入一个UDP的datagram比这个SO_SNDBUF大的话,EMSGSIZE将会返回. 因为UDP是不可靠的传输,所以这些buffer是不需要的. 接着UDP会加上它8字节的头部传给IP层,IP的在加上相应的头,然后通过路由函数决定去到哪个接口,然后将datagram添加到数据链路的queue中, 如果UDP发送一个超大的datagram是比TCP被切片的可能性更大的, 因为TCP在send socket buffer那里已经将应用层的数据限制了一部分了. 但是UDP并没有这样的机制.
当我们write函数成功返回的时候表示的也不是数据成功抵到对面了,而是说应用层的数据已经成功加入到datalink的队列中了.
接着我们看看SCTP的输出是什么样的形式?
这里写图片描述
我们可以看出,完全是和TCP一样的,只不过MSS等信息等存放在了cookie中了, 当我们收到write的成功返回时,表示的就是用户进程中的最后一个字节已经拷贝到了socket send buffer中了,用户进程的buffer能够重用了. 然后和TCP不同的之处就是SCTP的socket send buffer中的数据必须等待SACK的到来才能移除.

2.12 网络服务的标准

这里我们列出几个绝大多数TCP/IP实现的服务的标准和端口,你会发现,IANA尽然为这些简单的服务提供知名端口号,真是….不错:
这里写图片描述
在Unix中这些服务都是通过inetd这个守护进程大管家进行统一管理的,但是对于公司的大型服务器不能这样干,一般都是一台服务器上跑一个单独的程序进程都不够. 那么简单科普一下Unix的inetd: inetd是监视一些网络请求的守护进程,其根据网络请求来调用相应的服务进程来处理连接请求。它可以为多种服务管理连接,当 inetd 接到连接时,它能够确定连接所需的程序,启动相应的进程,并把 socket 交给它 (服务 socket 会作为程序的标准输入、 输出和错误输出描述符). 使用 inetd 来运行那些负载不重的服务有助于降低系统负载,因为它不需要为每个服务都启动独立的服务程序.
通过测试,google.com这个域内并没有配置daytime和echo服务当今的服务器也一般不会提供这种服务了,因为这是很容易被Dos攻击的入口, 我们一般telent google的Web服务器都是telnet google.com 80, 其实在我们的/etc/services文件中就定义了IANA给我们分配的知名服务的端口号map表,我们可以直接使用:
这里写图片描述

2.13 网络应用中的协议使用情况

我们先强调一点,ICMP是网络层的协议,和IP是同一层的协议:
这里写图片描述
traceroute应用是通过自己发送UDP包然后返回的ICMP进行分析的,不一定网络层必须要经过传输层的传输,ICMP的另一个应用程序ping就是中介通过IMCP返回的,traceroute也就只是自己产生了发送的udp包,返回也是直接ICMP返回的.


联系方式: reyren179@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值