WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.1 网络基础知识(2)
但是RFC文档中包含的仅是枯燥的技术规范及说明,并没有实现的具体步骤及方法示范。如果希望详细了解这些协议的方方面面,最权威的书籍莫过于W.Richard Stevens的遗作:3卷中译本的《TCP/IP详解》,它们是:
● 卷1:协议——TCP/IP协议指南,描述了属于每一层的各个协议以及它们如何在不同操作系统中运行。
● 卷2:实现——以约500个图例、15 000行实际操作的C代码来介绍TCP/IP协议是如何实现的。
● 卷3:TCP事务协议、HTTP、NNTP和UNIX域协议——主要内容包括TCP协议的扩展应用。
这3本书是网络编程资料中当之无愧的经典之作,惟一的遗憾是书的内容是以UNIX操作系统为背景的,但由于书中的例子代码是以标准C写的,所以对Windows的网络编程同样有很大的参考价值。
3. 数据包的封装
当应用层上的程序与其他计算机进行通信时,它所发送的数据包在TCP/IP模型的各个层次中逐渐下降,最后到达位于最底层的网络访问层中,网络访问层通过网络介质将数据包传送到目标主机后,数据包再逐渐上升到位于应用层的应用程序中,这个过程和数据在堆栈中的传送方式很相似,所以一般也把TCP/IP模型称为TCP/IP栈。
各种协议在实现的时候都需要互相交换信息,如IP协议需要交换地址信息,TCP协议需要交换控制信息,所以,不同的协议都会对要处理的数据进行适当的封装。
以通过HTTP协议访问一个网站为例,如图16.2所示,应用程序在用户数据的前面加上HTTP首部,组成一个数据包后准备通过TCP栈发送到目标网站,当数据包到达传输层的时候,TCP协议需要为数据包加上源端口和目标端口、数据包序号和应答字段等内容,以便实现连接应答与出错重传等TCP协议中的各种特征,这些数据当做TCP首部被加在原来数据包的前面,由此形成一个新的数据包后再交给Internet层的IP协议,这个被封装后的数据包就被称为TCP报文段(TCP Segment)。
图16.2 数据包的封装
IP协议负责寻址,它需要为TCP段加上目标IP地址,为了能让对方知道数据包是从哪里发过来的,也需要加上自己的IP地址,为了让目标主机的Internet层在处理数据包后能知道往传输层的哪个协议中送,还需要标出数据包是由传输层的哪个协议送下来的,这些数据形成一个IP首部后被附加在TCP报文段的前面,形成一个新的被称为IP数据报(IP Datagram)的新数据包。
现在IP数据报到了网络访问层的网卡驱动程序中,在这里驱动程序根据目标IP地址查出目标设备的MAC地址,在数据报的头部加上源MAC地址和目标MAC地址,同时为数据报加上一个尾部,所有这些数据形成一个数据帧(Frame)后被发送到网络介质上。
当数据帧到达目标主机后,网络访问层将以太网首部和尾部数据去除以恢复成IP数据报,并将IP数据报传递到上层的IP协议中;IP协议分析IP首部数据,并根据IP首部中的传输层协议类型将恢复的TCP报文段交到上层的TCP协议中;TCP协议再根据TCP首部中的数据判断数据包的序号,检测有无丢包,并根据情况决定是否要求发送方重传,当完成这些纠错任务后,将正确的数据交到应用层的应用程序中;这时的数据包中包含HTTP首部和用户数据,应用程序最后处理HTTP首部并得到正确的原始数据,一次数据包的传输就完成了。
当使用不同的协议时,各种协议将为数据包加上自己的首部,这些协议首部的数据定义各不相同,长度也各不相同,比如TCP首部的长度为20个字节,而UDP首部的长度只有8个字节。
正是由于每个层次的协议都对数据包进行封装,所以,在TCP/IP模型的不同层次进行编程的时候,数据的处理方法可能是完全不同的,比如实现一个Ping功能,如果在应用层上调用Icmp.dll中的相关函数,那么寥寥几句就可以完成,但是通过WinSock接口在Internet层上实现,那就要自己去处理ICMP协议首部和IP协议首部了。
16.1.2 一些重要概念
1. IP地址和子网掩码
要在计算机之间使用TCP/IP协议进行通信,这些计算机必须能够互相寻址,TCP/IP协议使用IP地址寻址方式,所以,对于一个IP网络来说,网络上的设备之间要进行通信的话,每个设备必须具有惟一的IP地址,否则将无法正确地进行寻址,同理,要让整个Internet范围内的计算机都能够互相通信,Internet上的所有设备也必须具有惟一的IP地址。
IP地址是一个32位的二进制数,为了用人们熟悉的10进制数表示,通常将它分为4个8位的二进制数,每个8位二进制数被转换成10进制,中间用小数点隔开,就得到了IP地址的10进制字符串格式,图16.3中的IP地址11000000 10101000 00000001 01100100,以10进制方式表示就是192.168.1.100。
图16.3 IP地址的两种表示格式
从理论上说,网络中的计算机之间要进行通信,只要有双方的IP地址就可以了,但是由于网络结构的关系,在实际使用中还需要将IP地址划分成网络号和结点号两部分。这是因为对一个小网络来说,所有的计算机都挂在同样的传输介质上,如果计算机A向计算机B发送IP数据报,A计算机的网卡将向传输介质发送一个封装有这个IP数据报的数据帧,数据帧头部的目标地址是B计算机的网卡MAC地址,实际上,这时网络上的任一台计算机都可以“听”到这个数据帧,只不过只有B计算机发现数据帧的目标MAC地址和自己符合而进行处理罢了。
如果让整个Internet的通信都以这种方式寻址,那么每台计算机发送的数据帧就要被传遍全世界才能保证被目标计算机“听”到,这显然是不现实的,所以一般来说要将整个网络划分成一个个小网络(子网),仅同一个子网中的计算机进行通信时才对目标MAC地址进行寻址,不同子网之间的IP数据报通过网关来转发,网关一般由路由器或者有路由功能的计算机来担任。
当计算机A向不属同一子网的计算机B发送数据时,它将以网关的MAC地址当做目标地址发送数据帧,由网关负责选择向其他最合适的子网转发。为了让计算机知道何时通过网关转发,必须能够分辨目标计算机是否在同一个子网上。
如何分辨目标计算机是否属于同一个子网呢,这就是子网掩码的作用。子网掩码用来将IP地址划分成两个部分:网络号和结点号。如果当前主机的IP地址和目标主机的IP地址的网络号相同,那么就说明它们同属一个子网。
假如需要在当前子网保留256个地址(结点),可以将IP地址中的最后8位视为结点号(因为8位二进制位可以表示256个不同的数),将剩下的24位视为网络号。为了划分网络号和结点号,可以使用一个高24位为1的32位二进制数去和IP地址进行and运算,这样得到的结果就是网络号,用这个数取反后和IP地址进行and运算,得到的就是结点号,用来进行and运算的数就是子网掩码。
以192.168.1.100地址为例,如图16.4所示,二进制IP地址11000000 10101000 00000001 01100100和子网掩码11111111 11111111 11111111 00000000进行and运算以后,得到网络号11000000 10101000 00000001,一般习惯在网络号后面用0补足32位并用10进制IP地址的形式来表达,这样就得到了网络号192.168.1.0。IP地址中余下的8位就是结点号。这个掩码用10进制IP地址的方式表示就是255.255.255.0。
图16.4 IP地址和子网掩码例一
再举一个例子,当子网里面的计算机不到16台的时候,可以只用4位来表示结点号。这时可以将掩码的全1数据位扩展到28位,如图16.5所示,前28位为1的子网掩码用IP地址方式表示就是255.255.255.240,因为最后8位11110000等于10进制数240。而网络号用0补足32位以后就是192.168.1.96,因为最后8位01100000等于10进制数96。当然,也可以在其他任何位置划分结点号和网络号,比如用16位来表示结点号的话,那么掩码就是255.255.0.0。
上页:第16章 TCP/IP和网络通信 · 16.1 网络基础知识(1) 下页:第16章 TCP/IP和网络通信 · 16.1 网络基础知识(3)