虚位以待(AD)
虚位以待(AD)
首页 > 操作系统 > Unix/BSD > UNIX网络编程笔记(1)—传输层协议

UNIX网络编程笔记(1)—传输层协议
类别:Unix/BSD   作者:码皇   来源:上善若水     点击:

开始学习网络编程的经典《UNIX网络编程》(第3版)作为研究生阶段的副本练习吧,厚厚一本书,希望能坚持看下去,坚持做些笔记。1 TCP IP协议概述IPv4网际协议版本4(Internet Protocol version 4

开始学习网络编程的经典《UNIX网络编程》(第3版)作为研究生阶段的副本练习吧,厚厚一本书,希望能坚持看下去,坚持做些笔记。


1.TCP/IP协议概述

IPv4

网际协议版本4(Internet Protocol version 4),32位地址,为TCP、UDP、SCTP、ICMP和IGMP提供分组递送服务。

IPv6

网际协议版本6(Internet Protocol version 6)。128位地址,为TCP、UDP、SCTP和ICMPv6提供分组递送服务。

TCP

传输控制协议(Transmission Control Protocol)。面向连接的传输层协议,提供可靠的全双工字节流,使用流套接字(stream socket)。

UDP

用户数据报协议(User Datagram Protocol)。UDP是一个无连接的传输层协议,使用数据报套接字(datgram socket)。

SCTP

流控制传输协议(Stream Control Transmission Protocol)。是可靠全双工关联的面向连接的传输层协议。SCTP是多宿的,每个关联的两端均涉及一组IP地址和一个端口号。这里多宿的概念简单理解就是多个网卡。

ICMP协议

网际控制消息协议(Internet Control Message Protocol)。ICMP处理在路由器和主机之间流通的错误和控制信息。这些消息由TCP/IP网络支持软件本身(而不是用户进程)产生和处理,例如ping和traceroute使用ICMP。

IGMP协议

网际组管理协议(Internet Group Management Protocol)。IGMP用于多播。

ARP(Address Resolution Protocol)和RARP(Reverse Address Resolution Protocol)

ARP地址解析协议把一个IPV4映射成一个硬件地址(以太网地址)。RARP反向地址解析协议把一个硬件地址映射成一个IPv4地址。


2.用户数据报协议(UDP)

UDP是无连接不可靠的传输层协议,着些主要体现在:
当用户进程往一个UDP套接字写入消息,消息随后被封装到一个UDP数据报(每个数据报都会有一个长度记录被接收端应用程序获取),进而又被封装到一个IP数据报,然后发往目的地,但是:UDP不保证数据报会到达其最终目的地,不保证各个数据报到达的先后顺序,也不保证数据报只到达一次


3.传输控制协议(TCP)

TCP向应用程序提供面向连接的可靠性服务

面向连接体现在:TCP客户端要首先与某个服务器建立一个连接,然后再夸该连接交换数据,最后终止连接。

可靠性体现在:TCP不保证数据一定会被对方端点接收,它提供的是数据的可靠递送(等待确认并自动重传)和故障的可靠通知(放弃重传或中断连接)。在数次重传失败后,TCP才会放弃,如此在尝试发送数据所花的时间一般为4~10分钟。

TCP具有的一些能力

可以动态估算客户和服务器之间的往返时间(round-trip time RTT),以便知道等待一个确认需要多少时间。例如RTT在局域网中几毫秒,在广域网则要数秒。

TCP对所发的数据进行排序:即在每个分节(TCP传递给IP的数据单元)的内部给每个字节关联一个序号,这就可以保证:在应用接收数据之前对非顺序到达的数据进行排序、并且对对端的重复数据进行丢弃。

TCP提供流量控制(flow control),流量控制确保发送端发送的数据不会使得接收端缓冲区溢出:当发送数据太快,而接收端来不及接收时,为了保证数据不丢失,必须协调好双方通信的节奏,通告窗口就起到了这样的作用:当接收来自发送端的数据时,窗口大小减小,当接收端从缓冲区读走数据时,窗口就变大;当窗口为0时,说明TCP对应某个套接字的缓冲区已满,此时它必须等待对端从缓冲区读取数据和不为0的通告窗口消息的到来。

TCP连接是全双工的(UDP也可以是全双工),也就是说在一个给定的连接上,应用可以在任何时刻在近处的两个方向上既发送数据又接收数据,需要的话也可以改成单工连接。


4.流控制传输协议(SCTP)

这个协议以前没听过,今天也学习一蛤。
SCTP支持多宿,与TCP相比提供两个IP地址之间的通信,SCTP可以提供两个系统之间的通信:一个端点可以有冗余的网络连接,每个网络又可以有各自接入因特网基础设施的连接,这样,乳沟某个网络或者通路发生故障,SCTP可以切换到使用已与该关联相关的另一个地址来规避故障。

SCTP是面向消息的。它提供各个记录的按序递送服务,与UDP记录数据报长度一样,SCTP也会写入每条记录的长度随数据一道传递给接收端应用。
PS:UDP是面向数据报,TCP是面向字节流的。

与TCP不同的是,当TCP字节丢失,将阻塞其后数据的递送,而SCTP则不会阻塞。


5.TCP的连接和终止

三路握手

我们知道TCP连接要经过三路握手,实际将发生下面这些情况:

服务器server端通过socket(创建套接字)、bind(绑定套接字)、listen(监听套接字)这三个过程完成,被称之为被动打开(passive open) 客户端client通过调用connect发起主动打开(active open)去向一个固定IP和固定端口号的服务器发起连接。这导致客户TCP发送一个SYN分节来告诉服务器将在接下来的发送数据时的初始序列号。SYN(Synchronous)是建立连接时使用的握手信号,通常SYN不携带数据只有一些报头。 服务器要给客户端回复一个ACK和一个SYN,ACK用来确认客户的SYN,SYN包含服务器在该连接中发送的数据的初始序列号。 最后客户必须确认第3点中服务器发送的SYN。
这里写图片描述

这就是三路握手的过程,所谓的三路握手,指的是这种服务器与客户端之间的交换至少需要3个分组。
根据图示,为什么客户发送SYNJ时(客户的初始序列号为J),服务器要回应$ACK_{J+1}(客户端期待的下一个序列号)?原因很简单,因为SYN所在字节本身也是一个序列号空间。

书中给出一个例子来说明TCP建立一次连接:

服务器端:
socket创建套接字=买一个手机
bind绑定套接字=办一张卡并把号码告诉别人
listen监听套接字=把手机开启为响铃模式
accept从等待连接队列中抽取一个连接,并创建一个新的套接字返回客户的标识=来电显示
客户端:
connect=拨打指定号码的电话

TCP选项

每个SYN可以有一些TCP选项,可以在待建立的连接中起到配置作用:

MSS选项:MSS=Maximum Segment Size,即最大分节大小,发送端TCP使用接收端的MSS值作为所发送分节的最大大小。 窗口规模选项:TCP使用滑动窗口进行流量控制,最大窗口大小是65535,这是因为TCP首部中配置窗口大小的相应字段占16位。 时间戳选项:这个选项针对于高速网络,以防止由失而复得的分组可能造成的数据损坏。

TCP连接终止

TCP建立一个连接需要3个分节,而终止一个连接需要4个分节,具体过程如下:

客户端首先调用close关闭套接字(主动关闭),该端的TCP会发送一个FIN分节。 服务器收到FIN后首先会发送一个ACK确认,同时会传递给本端接收应用进程一个文件结束符(EOF),这个结束符放在接收到的数据的缓冲队列的队尾,由于FIN的收到,也不会再收到额外数据了。 一段时间后,接收到文件结束符的应用调用close关闭套接字,此时服务端TCP也会发送一个FIN。 主动发起连接的一端收到FIN后会发送一个ACK作为确认。

每个方向都需要有一个ACK和一个FIN,所以说通常是4个字节。如图所示:

这里写图片描述

 

上述举例是客户端主动发起关闭(通常情况下是这样),实际上在某些协议(例如HTTP/1.0)可以由服务器执行主动关闭,事实上,在书中第1章给出的获取时间的demo中,都是服务器主动断开连接。

半关闭的概念:当服务器收到FIN之后,在它向客户端回应一个ACK和发送一个FIN之间(上述2,3步骤)是可以有数据流的,使用shutdown函数。

TCP状态转换图

TCP为一个连接定义了11种状态。这里就直接贴出电子书中截出的图。

这里写图片描述

图中可以看出,客户端和服务器都对应自己的状态转移图,并且此处不考虑客户端和服务器同时打开和同时关闭的情况,这种情况下,网络中会出现SYN或FIN交错的情况。可以使用netstat工具去监视状态的变化情况。

 

观察分组

书中给出了一个完整的TCP连接所发生的分组交换情况,这里也贴出图来:

这里写图片描述

 

过程很简单:客户服务器建立三次握手连接->客户与服务器进行数据交互->客户主动关闭连接

这里我比较在意的是两个MSS值的定义,图中箭头所示,网上看了不少资料得出如下结论:

IP数据包包头和TCP数据包包头分别是20bytes和20bytes MTU(最大传输单元)由硬件特性决定,以太网为例,其MTU=1500bytes MTU=IP报头+TCP报头+MSS 如果IP数据报大于MTU则IP报文就要分片传输,而TCP报文如果超过MSS,则TCP进行分段传输。 在IP不分片的前提下计算的得到MSS=1500-20-20=1460bytes Internet上标准的MTU是576bytes(MSS=576-20-20=536)

根据上述结论,就理解了书中1460和536的设定了。另外,服务器对客户请求的确认是伴随其应答发送的,这种做法被称作捎带(piggybacking),它通常在服务器处理请求并产生应答的时间少于200ms发生。

单从书中例子给出的交换来说,除了数据请求和数据应答,其余至少8个分节都是TCP的额外开销(除了建立连接和终止连接的7个分节外,还包括数据应答ACK)

SYN J
SYN K、 ACK J+1
ACK K+1
应答ACK
FIN M
ACK M+1
FIN N
ACK N+1


6.TIME_WAIT状态

书中单独拉出来作为一个小节出来,想必很重要吧。
首先,TIME_WAIT状态的停留时长是最长分节生命期(Maximum Segment Lifetime,MSL)的两倍,有时候称之为2MSL。
TIME_WAIT状态的存在有两个理由:

可靠地实现TCP全双工连接的终止 允许老的重复分节在网络中消逝

第1个理由很好理解:假设服务器端发送FIN N,而客户端的第一个ACK N+1因为某些原因丢失了,这个时候服务器久久没有收到ACK就会超时重传一个FIN N,那么此时客户端必须维护当前的状态信息,这样它才能重新发送那个最终的ACK N+1。
更广义的说法:如果连接双方的某一端发起了主动关闭连接,那么它最终会进入TIME_WAIT状态,因为如果出现异常最终重传的ACK就是由主动发起关闭连接的那一端发送的。

第2个理由就有点绕口了,我们假设同一个IP和端口之间有一个TCP连接,我们关闭这个连接,过一段时间后在相同的IP地址和端口号之间建立另一个连接。后一个连接成为前一个连接的化身,因为他们的IP地址和端口号相同。TCP防止某个老的重复的分组,在该连接终止后再现,从而被误解成属于同一连接的某个化身。为了做到这一点,TCP将不给处于TIME_WAIT状态的连接发起新的化身,(这句话我的理解是,如果在连接双方,某一端进入了TIME_WAIT状态,那么,TCP将不会再在连接双方所对应的IP和端口发起连接,即所谓的化身,此时这个即将断开的连接中还有多的重复的分组)。既然TIME_WAIT状态持续时间是MSL的两倍,那么这个时间足以让某个方向上的分组最多存活MSL秒就被丢弃,另一个方向上的应答最多存活MSL也被丢弃。

总结这两个理由可以归结为两句话:

**1. 万一在最中确认连接中断时服务器的ACK回应丢失怎么办,这样就必须再次重传FIN。
2. 万一在极短的时间内从新有一个相同套接字连接建立怎么办,这样就必须处理完上一次数据。**


7.SCTP简介

SCTP也是面向连接的,因而也有关联的建立和终止的握手过程。与tcp不同的是,建立连接需要4个分节(四路握手),关闭连接需要3个分节。SCTP的思路握手跟TCP的三路握手类似,差别在于作为SCTP整体一部分的cookie的生成,这个cookie包含了服务器IP地址清单、初始序列号等内容。SCTP重要程度次于TCP,这里仅仅贴出书中示意图。

四路握手示意

 

这里写图片描述

 

关联终止示意

 

这里写图片描述

 

SCTP中的分组交换

 

这里写图片描述

 


8.端口号

多个进程可能同时使用传输层协议,端口号(16位整数)用来区分这些进程。
众所周知的端口号:0-1023
已登记端口号:1024-49151
动态或者私用端口:49152-65535
常用端口号:

协议 端口号
HTTP协议代理服务器常用端口号 80、8080、3128、8081、9080
FTP(文件传输)协议代理服务器常用端口号 21/TCP
TFTP(Trivial File Transfer Protocol ),默认的端口号 69/UDP
SMTP Simple Mail Transfer Protocol (E-mail),默认的端口号 25/TCP
SSH(安全登录)、SCP(文件传输)、端口重定向,默认的端口号 22/TCP

另外,客户通常使用短期存货的临时端口号,这些端口号由传输层协议自动赋予客户。

套接字与套接字对

套接字:标识每个端点的两个值IP地址和端口号
套接字对:定义一个TCP连接的两个端点的四元组{本地IP:本地端口号,外地IP:外地端口号}

并发服务器中主服务器循环派生子进程来处理新的连接。
TCP通过套接字对来区分连接,比如说:
在服务器端,父进程的监听套接字:{:21,:*}
fork子进程1的已连接套接字:{本地ip:21,外地ip1:外地端口号1}
fork子进程2的已连接套接字:{本地ip:21,外地ip2:外地端口号2}


9.缓冲区大小限制

知道下面一些概念:
1. ipv4数据报最大大小是65535字节,包括ipv4的首部,这个原因是因为在IPV4报头中用以表示其总长度的字段只有16位。ipv6是65575字节=65535+40,40表示ipv6首部。(ipv6中净长度不包括首部)
2. MTU在上文中提到,由链路层的硬件规定,譬如说以太网的MTU是1500字节,IPv4要求的最小链路MTU是68字节。IPV6要求最小链路MTU是1280字节。
3. 关于分片的说法,当一个IP数据报从某个接口出去的时候,如果它的大小超过相应链路的MTU,则IPv4和IPv6将执行分片(fragmentation),在Windows下,可以通过ping命令来查看本机的MTU大小,例如:

    ping -l 1800 -f www.baidu.com

-f选项给IPv4首部设置不分片(dont fragment)即DF位,而-l选项表示向目标主机发送多少字节的数据,虽然理论上来说最大可以是65535字节,然而在DOS下ping -l最大是65500字节(因为windows的漏洞所致),好了这些都不是重点。通过ping的方式,我们可以知道本机设置的MTU的大小(一般小于1500),当我们向目标主机发送大于MTU的数据并且强制设置不分片,系统就会提示错误:

需要拆分数据包但是设置DF

意思显而易见就是说,你要发送这么多数据超过了我MTU的大小现在必须要分片了,可是你却又设置不分片。


10.TCP&UDP输出

TCP

当某个应用进程写数据到一个TCP套接字中时将经过下面步骤:

1.每个TCP套接字有一个发送缓冲区,我们可以使用SO_SNDBUF套接字选项来更改缓冲区大小。
2.当某个应用进程调用write时,内核从该应用进程的缓冲区复制所有数据到所写套接字的发送缓冲区。
3.write函数是阻塞调用,也就是说,当套接字的发送缓冲区大小小于应用进程的数据时,应用进程将被投入休眠,直到应用进程缓冲区中的数据全部被写入到套接字发送缓冲区,这也就意味着write函数调用成功返回,并不能说明对端已经接受到了数据。
4.每个发送端TCP套接字缓冲区需要保留数据副本直到被确认。
5.本端TCP以小于等于MSS大小的块加上TCP首部构成TCP分节传递给IP。(这里提到了一个最小重组缓冲区字节数,查了一下,这代表IPv4和IPv6都必须保证支持的最小数据报大小其中IPv4是576字节,IPv6是1500字节)
6.最后,IP层给每个TCP分节安上一个IP首部,并且根据TCP分节中提供的对端IP地址查找路由表项以确定外出接口,然后把数据报传递给相应额的数据链路。

UDP

当某个应用进程写数据到一个UDP套接字中时将经过下面步骤:

1.UDP套接字没有发送缓冲区,但是却有一个SO_SNDBUF套接字选项来修改发送缓冲区的大小(这表示应用进程写入该套接字的UDP数据报大小的上限。)
2.因为UDP是不可靠的,发送端不必保存数据的副本。
3.UDP数据报首部是8个字节,传给IP层后,又安上相应的IP首部构成IP数据报,执行路由操作确定外出接口,或者直接把数据报加入数据链路层输出队列。
4.UDP没有MSS因此比TCP更容易分片。
5.write返回成功表示缩写数据报已经被加入数据链路层输出队列。

相关热词搜索: