在进行网络编程之前,我们必须要对网络通信的基础知识有个大概的框架,TCP/IP协议族涉及到多种网络协议,一般说TCP/IP协议,它不是指某一个具体的网络协议,而是一个协议族。本篇章主要针对IP协议、TCP和UDP协议记录总结。
OSI七层参考模型是国际标准化组织(ISO)制定的一个用于通信系统间网络互联的标准体系,称为OSI参考模型或七层模型。下面对七层模型简单介绍:
利用传输介质(双绞线、电缆等)为数据链路层提供物理连接,实现比特流的透明传输;简单的说就是数据的传输通道,传送电平信号,数据单位是比特(bit)。
该层设备有集线器、网线、中继器等。
负责建立和管理节点间逻辑连接、进行硬件地址寻址、差错检测等功能。
接收来自物理层的位流形式的数据,并封装成数据帧格式传送到上一层;或者把上层的数据帧拆装为位流形式的数据再转发到物理层。数据的单位称为帧。
该层设备有网桥、交换机等。
负责将数据传输到目标地址,就是路由和寻址,数据单位称为包;IP协议就处于网络层。
该层设备有路由器。
为上层协议提供端到端的可靠和透明的数据传输服务,包括差错校验处理和流控等。数据单位称为段。TCP、UDP协议处于传输层中。
负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。将不同实体之间表示层的连接称为会话。因此会话层的任务就是组织和协调两个会话进程之间的通信,并对数据交换进行管理;数据单位是报文。
提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别;数据单位是报文。
为上层用户提供应用接口,也为用户直接提供各种网络服务;数据单位是报文。常见协议有HTTP、FTP、DNS等。
TCP/IP分层模型有两种:四层和五层模型,它们是对OSI七层模型的简化。
了解了各层之间的框架后,我们就会好奇,各层次之间是怎么互相配合通信的呢?(以下针对TCP/IP协议模型讲解)
当应用程序传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络,其中每一层对收到的数据都要增加一些首部信息(有时还要增加尾部信息),这叫做对数据的封装。应用层要发送的数据经过层层封装传输到目标机,目标机从最底层一步步将封装的数据拆解到应用层,最终还原了数据的本身形态。
举例说明:假如应用程序使用TCP协议发送数据“hello”,从上到下,首先经过应用层,在应用层将原始数据“hello”添加APP首部,此时数据变成了“APP首部+hello”,然后依次通过传输层、网络层,最后达到网络接口层,在最底层数据就被封装成了“各层的首部信息+hello”,将这些数据信息通过传输介质(例如网线)以二进制的形式发送比特流到目标机,目标机接收到封装后的数据后,从最底层到最高层,逐层拆包(大概的意思就是网络层就将IP首部拆除、输出层将TCP首部拆除),最后还原数据“hello”。
每个网络主机都会有一个IP地址,每个节点通过IP地址进行区分不同的主机,从而进行通信,路由器和主机都有一个IP地址。该IP地址是软件层面的一个地址,如192.168.xxx.xxx,而不是硬件层面的MAC地址。
IP地址分为IPv4地址和IPv6地址。IPv4地址有32位正整数表示,IPv6地址由128位正整数表示。
现在我们用的都是IPv4地址,将32位的 IP 地址以每8位为一组,分成4组,每组以 “.” 隔开,再将每组数转换成十进制数。
IP地址由网络标识和主机标识组成,可以通过子网掩码来确定网络地址和主机地址分别占用多少位。其中网络标识 = IP地址 & 子网掩码。
不含选项字段的IP首部包含20个字节,如下图所示。
其中32位源IP地址指明发送端主机的IP地址,32位目的IP地址指明接收端主机的IP地址,这样我们就可以知道数据从哪里来,要发往何处了。
如果目的主机与源主机直接相连(如点对点链路)或都在一个共享网络上(以太网或令牌环网),那么IP数据报就直接送到目的主机上。否则,主机把数据报发往一默认的路由器上,由路由器来转发该数据报。
IP层既可以配置成路由器的功能,也可以配置成主机的功能。本质上的区别在于主机从不把数据报从一个接口转发到另一个接口,而路由器则要转发数据报。
IP层可以从TCP、UDP、ICMP、IGMP或者网络接口接收数据报并发送,IP层在内存中有一个路由表,当IP层接收并发送数据报时都会去检索下路由表,检查目的IP地址是否为本机的IP地址之一或者IP广播地址,如果不是,被设置成路由器功能的IP层就会对数据报进行转发(没设置成路由器功能的IP层会把该数据报废弃)。
IP地址可以分为5类:A类、B类、C类、D类和E类。我们常用的第一个字节为192的处于C类。
分配主机地址时,不可以全部为 0 或全部为 1。因为全部为 0 只有在表示对应的网络地址或 IP 地址不可以获知的情况下才使用,而全部为 1 的主机通常作为广播地址。
A类IP地址由1个字节网络地址(网络地址最高位必须为“0”)和3个字节主机地址组成。
网络地址范围是0~127;
主要分配给具有大量主机而局域网络个数较少的大型网络使用;
广播地址为:XXX.255.255.255;
A类地址中设有私有地址和保留地址:
10.X.X.X:是私有地址,在互联网中不能使用,而被用在局域网中的地址;
127.X.X.X:是保留地址,也称为环回地址,主要用来测试网络协议是否工作正常,比如在电脑中使用ping命令去ping 127.1.1.1就可以测试本地TCP/IP协议是否正常。
0.0.0.0:只能用作源地址。0.0.0.0是不能被ping通的,在服务器中,0.0.0.0并不是一个真实的的IP地址,它表示本机中所有的IPv4地址。监听0.0.0.0的端口,就是监听本机中所有IP的端口。
B类IP地址由2个字节的网络地址(网络地址最高两位必须为“10”)和2个字节的主机地址组成。
网络地址范围是128~191;
一般分配给中型网络使用;
广播地址为:XXX.XXX.255.255;
B类地址中设有私有地址和保留地址:
172.16.0.0 ~ 172.31.255.255:是私有地址;
169.254.X.X:是保留地址。
C类IP地址由3字节的网络地址(网络地址最高三位必须为“110”)和1字节的主机地址组成。
网络地址范围是192~223;
广播地址为:XXX.XXX.XXX.255;
分配给小型网络使用,如一般的局域网和校园网;
192.168.X.X:是私有地址。
D类IP地址第一个字节以“1110”开始,不分网络地址和主机地址,它是一个专门保留的地址,它并不指向特定的网络,目前这一类地址被用在多点广播(多播),多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。
地址范围:224.0.0.0~239.255.255.255 。
E类IP地址以“1111”开始,为将来使用保留。
地址范围:240.0.0.0 ~ 255.255.255.255。
除了IP地址以外,主机还需要知道有多少比特用于子网号及多少比特用于主机号。这是在引导过程中通过子网掩码来确定的。子网掩码是一个32 bit的值,其中值为1的比特留给网络号和子网号,为0的比特留给主机号。
如下图所示:B类地址的两种不同的子网掩码格式,一个子网号和主机号都是8 bit宽,另一个划分成10 bit的子网号和6 bit的主机号。
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的流协议。
何为面向连接呢?
就是说两个使用TCP协议的应用(通常是一个客户端和一个服务端),在互相通信交换信息之前,必须先建立一个TCP连接,即进行三次握手,互相确认身份后连接。,连接上后,TCP就可以进行全双工通信了。
可靠又从何而来?
① 应用数据被分割成TCP认为最适合发送的数据块(由TCP传递给IP的信息单位称为报文段)。
② 当TCP发出一个报文段后,它会启动一个定时器,等待目的端确认收到这个报文段。如果不能
及时收到一个确认,将重发这个报文段。(即发送端发送的每个TCP报文段都必须得到接收方的应答,才能认为这个TCP报文段传输成功)
③ 当TCP收到发自TCP连接另一端的数据,它将发送一个确认(这个确认不是立即发送,通常将延迟几分之一秒才发送)。
④ TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错, TCP将丢弃这个报文段和不确认收到此报文段(希望发送端超时并重发)。
⑤ TCP报文段作为IP数据报来传输,而IP数据报的到达可能会乱序,同理TCP报文段的到达也可能会乱序,由于重传机制,也可能会收到重复的数据;所以TCP协议还会将接收到的TCP报文段重排、整理、再交付给应用层。
⑥ TCP还提供流控制,客户端和服务端都有固定大小的缓冲空间,发送或接收的数据不能超过这个缓冲空间。
流服务:
两个应用程序通过TCP连接交换8 bit字节构成的字节流,TCP不在字节流中插入记录标识符,我们将其称为字节流服务。
TCP报文段的格式如下,TCP首部如果没有其它选项的存在,仅占20字节。
源端口号和目的端口号:用于寻找发送端和接收端的应用进程,这两个值加上IP首部中的源端IP地址和目的端IP地址就可以确定唯一一个TCP连接。IP地址加端口号也称为一个插口(socket)。
seq序号:用来标识从TCP发送端向TCP接收端发送的数据字节流,它的值表示在这个报文段中的第一个数据字节所处位置码,TCP协议会对发送或者接收的数据进行编号(按字节的形式)。
在TCP传送的数据流中,每一个字节都有一个序号。假如当前报文段的序号为100,且数据占100字节,则下一个报文段的序号就是200,序号到达2^32 - 1后又从0开始。
ack确认序号:期望收到的下一个报文段的首部中的序号,只有ACK标志为1时,确认序号才有效。
无法对数据流中选定的部分进行确认;例如,如果1~1024字节已经成功收到,下一报文段中包含序号从2049~3072的字节,收端并不能确认这个新的报文段,它所能做的就是发回一个确认序号为1025的ack。
也无法对一个报文段进行否认;例如,如果收到包含1025~2048字节的报文段,但它的检验和错,TCP接收端所能做的就是发回一个确认序号为1025的ack。
首部长度:仅占4 bit,表示TCP首部的总长度,4字节为一个单位计算,所以TCP首部总长度不能超过60(15*4)字节。
URG:紧急指针字段标志,如果是1表示紧急指针字段有效;
ACK:只有当ACK=1时,确认序号字段才有效;
PSH:为1时,接收方应该尽快将本报文段立即传送给应用层;
RST:为1时,表示出现连接错误,必须释放连接,然后再重建传输连接;还用来拒绝一个不法的报文段或拒绝打开一个连接;
SYN:“SYN=1,ACK=0”时表示请求建立一个新连接,带SYN标志的TCP报文段为同步报文段;
FIN:为1表示发送方没有数据要传输了,要求释放连接。
窗口大小:用于流控制,单位为字节,起始于ack确认序号字段指明的值,这个值是接收端正期望接收的字节。它是一个16 bit字段,因而最大为65535字节。
检验和:包含了TCP首部和TCP数据,是一个强制性的字段,由发送端计算和存储,并由接收端进行验证。
紧急指针:是一个正的偏移量,和seq序号字段中的值相加表示报文段最后一个字节的序号。
端口号用来标识能进行网络通信的进程,取值范围为0~65535。
一个主机通常只有一个IP地址,但是可以有多个端口号,每个端口号表示一个能上网的进程。
一个拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务都是能够进行网络通信的进程,而IP地址只能区分网络中不同的主机,并不能区分主机中的这些进程,显然不能只靠IP地址,因此才有了端口号。通过“IP地址+端口号”来区分主机不同的进程。
服务端中的这些服务有默认的端口号:
HTTP服务:默认端口号为80;
FTP服务:默认为21;
SSH服务:默认为22;
Telnet服务:默认为23。
CLOSED状态:表示一个初始状态;
LISTENING状态:表示服务端的某个SOCKET处于监听状态,监听客户端的连接请求;
SYN_SENT状态(客户端):表示客户端已发送SYN报文;
SYN_REVD状态(服务端):表示服务端接受到了SYN报文;
ESTABLISHED状态:表示连接已经建立;
FIN_WAIT_1状态:在ESTABLISHED状态时,主动关闭连接,向对端发送了FIN=1报文;
FIN_WAIT_2状态:对端接收到FIN=1报文,然后回应ACK=1报文后,则主动发起关闭端进入到该状态;
TIME_WAIT状态:表示收到了对端的FIN=1报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了;
LAST_ACK状态:它是被动关闭的一方,在发送FIN=1报文后,最后等待对方的ACK报文;
CLOSE_WAIT状态:表示等待关闭。
TCP是一个面向连接的协议,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。双方建立连接的方式也叫做“握手”,需要进行三次“握手”才能完全建立起通信连接。
三次握手工作过程(客户端发起、服务端监听):
第一次握手:客户端将报文段TCP首部中的SYN置为1,随机产生一个seq序号为J的值,然后把该数据包发送给服务端,表示请求建立连接,发送完成后,客户端会进入SYN_SENT状态,等待服务端确认。
第二次握手:服务端接收到客户端发过来的数据包后,解析发现SYN被置1了,知道客户端在请求建立连接了,于是,服务端将标志位SYN和ACK都置1,且随机生成一个seq序号为K的值,ack确认序号置为J+1的数据包发送到客户端,以确认客户端的连接请求,服务器端发送完后就会进入到SYN_RCVD状态。
第三次握手:客户端接收到服务端发来的确认数据包后,解析ack确认序号是否为J+1,ACK标志位是否为1,如果正确,则将ACK标志位置为1,ack确认序号置为K+1,然后把数据包发送给服务端,发送完后客户端进入ESTABLISHED状态;服务端接收到数据包后,检查ack是否为K+1,ACK是否为1,如果正确则服务端也进入ESTABLISHED状态,连接建立成功,自此客户端和服务端之间就可以正常通信了。
因为TCP的通信是全双工的,每个通信方向都要单独关闭,所以断开总共需要四个数据包来确认连接的断开。这四个通信过程我们叫它“四次挥手”。发起断开请求可以是客户端,也可以是服务端。
以下以客户端发起断开请求为例:
第一次挥手:客户端将FIN=1、随机生成seq序号值为M的数据包发给服务端,表示主动发起断开请求,发送完成后客户端进入FIN_WAIT_1状态,等待服务端应答;
第二次挥手:服务端接收到FIN=1的断开连接请求数据包,将标志位ACK置为1,ack确认序号置为M+1的数据包发回给客户端,表示确认了单方面的断开请求,此时客户端进入FIN_WAIT_2状态,而服务端此时还有数据要跟客户端通信,连接还没有完全关闭,等待服务端也发送FIN=1数据包;
第三次挥手:当服务端已经没有数据要发送了,则向客户端发送FIN=1、seq序号为N的数据包,表示断开连接请求,此时服务端进入LAST_ACK状态;
第四次挥手:客户端接收到服务端发来的FIN=1数据包后,回发一个ACK=1、ack确认序号为N+1的数据包,客户端此时进入TIME_WAIT状态,等待2ms延迟没收到服务端信息后就进入关闭状态,服务端接收到应答数据后就断开了连接,进入关闭状态。
UDP(User Datagram Protocol):用户数据报协议,是一种无连接、不可靠的协议。
不可靠性体现在它把应用程序传给IP层的数据包发送出去,可以确保发送消息的大小,却不能保证消息一定会到达,出现差错直接丢弃,无反馈机制。
由于它不像TCP协议那样复杂,无连接、重发机制、应答机制等,所以它的传输速度会较快。
UDP协议传输出现错误的概率是很小的,并且它的实时性非常好,常用于实时视频的传输,比如直播、网络电话等,所以,UDP协议还是会被应用与对传输速度有要求,并且可以容忍出现差错的数据传输中。
源端口号和目的端口号同TCP一样;
UDP长度:指的是UDP首部和UDP数据的字节长度;
UDP检验和:包含UDP首部和UDP数据,UDP的检验和是可选的,而TCP的检验和是必需的。
参考文章:
TCP_IP详解卷1
正点原子嵌入式Linux C应用编程指南