前言:你是否曾有以下苦恼:在编程时虽然会调用网络方面的 APIip对讲源码,却不清楚具体原理;会用基本的 ifconfig 等命令,却不太理解其输出;对于长长的 MAC 地址、IP 地址和子网掩码,不了解它们的分配机理;看了网上对路由器、交换机、TCP、UDP、DHCP、DNS 等知识点的介绍,却依旧迷茫。不必担心,你将会循序渐进地牢牢掌握网络方面的知识。围绕 TCP/IP 协议的网络知识非常重要,ip对讲源码我们今天使用电脑手机上网聊天、游戏、购物、追剧等,其实都在不自觉地使用 TCP/IP 协议。想要成为编程方面的专家,除了数据结构、算法、设计模式等知识,网络方面的知识也不可或缺。
一、传输层报文1、TCP数据包的头
typedef struct _TCP_HEADER { USHORT nSourPort ; // 源端口号16bit USHORT nDestPort ; // 目的端口号16bit UINT nSequNum ; // 序列号32bit UINT nAcknowledgeNum ; // 确认号32bit USHORT nHLenAndFlag ; // 前4位:TCP头长度;中6位:保留;后6位:标志位16bit USHORT nWindowSize ; // 窗口大小16bit USHORT nCheckSum ; // 检验和16bit USHORT nrgentPointer ; // 紧急数据偏移量16bit} TCP_HEADER, *PTCP_HEADER ;2、UDP数据包的头
typedef struct _UDP_HEADER { USHORT nSourPort ; // 源端口号16bit USHORT nDestPort ; // 目的端口号16bit USHORT nLength ; // 数据包长度16bit USHORT nCheckSum ; // 校验和16bit} UDP_HEADER, *PUDP_HEADER ;进入协议栈的过程:(从协议栈出来刚好相反)
二、网络层报文1、IP头
IP数据包也叫IP报文分组,传输在ISO网络7层结构中的网络层,它由IP报文头和IP报文用户数据组成,IP报文头的长度一般在20到60个字节之间,而一个IP分组的最大长度则不能超过65535个字节。
下图为IP分组的报文头格式,报文头的前20个字节是固定的,后面的可变
版本号 : 4 个 bit ,用来标识 IP 版本号。这个 4 位字段的值设置为二进制的 0100 表示 IPv4 ,设置为 0110 表示 IPv6 。目前使用的 IP 协议版本号是 4 。
首部长度 : 4 个 bit 。标识包括选项在内的 IP 头部字段的长度。
服务类型 : 8 个 bit 。服务类型字段被划分成两个子字段: 3bit 的优先级字段和 4bit TOS 字段,最后一位置为 0 。 4bit 的 TOS 分别代表:最小时延,最大吞吐量,最高可靠性和最小花费。 4bit 中只能将其中一个 bit 位置 1 。如果 4 个 bit 均为 0 ,则代表一般服务。
现在大多数的 TCP/IP 实现都不支持 TOS 特性,但自 4.3BSD Reno 以后的新版系统都对它进行了设置。另外, OSPF 和 IS-IS 都可以根据这些字段的值进行路由策略。而类似 SLIP 这样的协议虽然提供基于服务类型的排队 *** ,允许对交互型数据优先进行处理,但它的这种排队机制由 SLIP 自身来判断和处理。驱动程序会先查看协议字段(确定是否是 TCP 段),然后检查 TCP 信源和信宿的端口号来判断是否是一个交互服务。
最近, TOS 字段已经作为区分服务( Diffserv )架构的一部分被重新定义了。 Diffserv 比 TOS 定义所允许的处理更加灵活。在 Diffserv 下,能够在一台路由器上定义服务分类( COS ),将数据包归类到这些分类中去。路由器可以根据它们的分类使用不同的优先级对数据包进行转发。每一个排序和转发处理称为一个 PHB 。这个架构也被简称为 COS 。
利用开始的 6 个位构成 DSCP 位,可以使用任意数值或根据区分服务体系结构中预先定义的服务类别,最多可以定义 64 个不同的服务类别并整理到 PHB 中。 ECN 为显式拥塞通知位。当路由器支持该特性时,这些位可以用于拥塞信号( ECN=11 )。
总长度字段 : 16 个 bit 。接收者用 IP 数据报总长度减去 IP 报头长度就可以确定数据包数据有效负荷的大小。 IP 数据报最长可达 65535 字节。
标识字段 : 16 个 bit 。唯一的标识主机发送的每一份数据报。接收方根据分片中的标识字段是否相同来判断这些分片是否是同一个数据报的分片,从而进行分片的重组。通常每发送一份报文它的值就会加 1 。
标志字段 : 3 个 bit 。用于标识数据报是否分片。第 1 位没有使用,第 2 位是不分段( DF )位。当 DF 位被设置为 1 时,表示路由器不能对数据包进行分段处理。如果数据包由于不能分段而未能被转发,那么路由器将丢弃该数据包并向源发送 ICMP 不可达。第 3 位是分段( MF )位。当路由器对数据包进行分段时,除了最后一个分段的 MF 位被设置为 0 外,其他的分段的 MF 位均设置为 1 ,以便接收者直到收到 MF 位为 0 的分片为止。
位偏移 : 13 个 bit 。在接收方进行数据报重组时用来标识分片的顺序。用于指明分段起始点相对于报头起始点的偏移量。由于分段到达时可能错序,所以位偏移字段可以使接收者按照正确的顺序重组数据包。当数据包的长度超过它所要去的那个数据链路的MTU时,路由器要将它分片。数据包中的数据将被分成小片,每一片被封装在独立的数据包中。接收端使用标识符,分段偏移以及标记域的MF位来进行重组。
生存时间 : 8个bit 。 TTL域防止丢失的数据包在无休止的传播。该域包含一个 8 位整数,此数由产生数据包的主机设定。 TTL 值设置了数据包可以经过得最多的路由器数。 TTL的初始值由源主机设置(通常为 32 或 64 ),每经过一个处理它的路由器, TTL 值减 1 。如果一台路由器将 TTL 减至 0 ,它将丢弃该数据包并发送一个 ICMP超时消息给数据包的源地址。注意: TTL值经过PIX时不减1。
协议字段 : 8 个 bit 。用来标识是哪个协议向 IP 传送数据。 ICMP 为 1 , IGMP 为 2 , TCP 为 6 , UDP 为 17 , GRE 为 47 , ESP 为 50 。
首部校验和 :根据 IP 首部计算的校验和码。
源IP地址:32位(bit),4个字节,每一个字节为0~255之间的整数,及我们日常见到的IP地址格式。
目的IP地址:32位(bit),4个字节,每一个字节为0~255之间的整数,及我们日常见到的IP地址格式。
Option 选项 :是数据报中的一个可变长的可选信息。
选项字段以32bit 为界,不足时插入值为0填充字节。保证IP首部始终是 32bit 的整数倍。由于Delphi里面没有位域这个概念,所以定义结构的时候只能整字节了,挺怀恋C++或者Erlang的,有位域定义出来和使用起来都很方便了 。
//IP包 TIPHeader = packed record iph_verlen: byte; // 版本和长度 iph_tos: byte; // 服务类型 iph_length: word; // 总长度,2个无符号字节所以只能65535 iph_id: word; // 标识 iph_offset: word; // 标志和片偏移 iph_ttl: byte; // 生存时间 iph_protocol: byte; // 协议 iph_xsum: word; // 头校验和 iph_src: longword; // 源地址 iph_dest: longword; // 目的地址 end;这个结构体有什么用呢?其实在嗅探的时候就很有用了。
2、ICMP头和报文校验和的计算
//定义ICMP包头typedef struct _ICMP_HEADER { BYTE bType ; // 类型8bit BYTE bCode ; // 代码8bit USHORT nCheckSum ; // 校验和16bit USHORT nId ; // 标识,本进程ID16bit USHORT nSequence ; // 序列号16bit UINT nTimeStamp ; // 可选项,这里为时间,用于计算时间32bit} ICMP_HEADER, *PICMP_HEADER ;送ICMP报文时,必须由程序自己计算校验和,将它填入ICMP头部对应的域中。校验和的计算 *** 是:
将数据以字(16位)为单位累加到一个双字中(强转换双字类型),如果数据长度为奇数(奇数个字节),最后一个字节将被扩展到字,累加的结果是一个双字,最后将这个双字的高16位和低16位相加后取反,便得到了校验和。
// 计算ICMP包校验值// 参数1:ICMP包缓冲区// 参数2:ICMP包长度USHORT GetCheckSum ( LPBYTE lpBuf, DWORD dwSize ){ DWORD dwCheckSum = 0 ; USHORT* lpWord = (USHORT*)lpBuf ; // 累加 while ( dwSize > 1 ) { dwCheckSum += *lpWord++ ; dwSize -= 2 ; } // 如果长度是奇数 if ( dwSize == 1 ) dwCheckSum += *((LPBYTE)lpWord) ; // 高16位和低16位相加 dwCheckSum = ( dwCheckSum >> 16 ) + ( dwCheckSum & 0xFFFF ) ; // 取反 return (USHORT)(~dwCheckSum ) ;}相关文章
本站已关闭游客评论,请登录或者注册后再评论吧~