精华内容
下载资源
问答
  • 一、以太网 所有的以太网(802.3)都基于一个共同的格式。...以太网开始是一个前导字段,接收器电路用它确定一个的到达时间,并确定编码位 (称为时钟恢复)之间的时间量。由于以太网是一个异步的局域网...

    一、以太网帧

    • 所有的以太网(802.3)帧都基于一个共同的格式。在原有规范的基础上,帧格式已被改进以支持额外功能

    以太网帧格式

    • 下图显示了当前的以太网帧格式,以及它与IEEE提出的一个相对新的术语IEEE分组(一个在其他标准中经常使用的术语)的关系

    • 以太网帧开始是一个前导字段,接收器电路用它确定一个帧的到达时间,并确定编码位 (称为时钟恢复)之间的时间量。由于以太网是一个异步的局域网(即每个以太网接口卡中不 保持精确的时钟同步),从一个接口到另一个接口的编码位之间的间隔可能不同。前导是一 个公认的模式(典型值为0xAA),在发现帧起始分隔符(SFD)时,接收器使用它“恢复时钟”
    • SFD的固定值为0xAB
    • 这个基本的帧格式包括48位(6字节)的目的地址(DST)和源地址(SRC)字段。这些地址有时也采用其他名称,例如“MAC地址”、 “链路层地址”、 “802地址”、 “硬件地址” 或“物理地址”。以太网帧的目的地址也允许寻址到多个站点(称为“广播”或“组播”)。广播功能用于ARP协议,组播功能用于ICMPv6协议,以 实现网络层地址和链路层地址之间的转换
    • 源地址后面紧跟着一个类型字段,或一个长度字段。在多数情况下,它用于确定头 部后面的数据类型。TCP/IP网络使用的常见值包括IPv4 (0x0800)、 IPv6 (0x86DD)和 ARP (0x0806)。0x8100表示一个Q标签帧(可携带一个“虚拟局域网”或802.1q标准 回 VLAN ID)。一个以太网帧的基本大小是1518字节,但最近的标准将该值扩大到2000字节
      • 注意:最初的IEEE (802.3)规范将长度/类型字段作为长度字段而不是类型字段使用。困此,这个字段被重载(可用于多个目的)。关键是看字段值。目前,如果字段值大于或等于1536,则该字段表示类型,它是由标准分配的超过1536的值。如果字 段值等于或小于1500,则该字段表示长度。 [ETHERTYPES]给出了类型的完整列表
    • 在上述字段之后,[802.3-2008]提供了多种标签包含由其他IEEE标准定义的备种协议字段。其中,最常见的是那些由802.1p和802.1q使用的标签,它提供虚拟局域网和一些服务质量(Qos)指示符
      • 注意:当前的[802.3-2008]标准采用修改后的802.3帧格式,提供最大为482字节 的“标签”,它携带在每个以太网帧中。这些较大的帧称为信封帧,长度最大可能达到2000字节。包含802.1p/q标签的帧称为Q标签帧,也是信封帧。但是,并非 所有信封帧必然是Q标签帧
    • 在这些讨论过的字段之后,是帧的数据区或有效载荷部分。这里是放高层PDU(例如IP数据报)的地方。传统上,以太网的有效载荷一直是1500字节,它代表以太网的MTU。目 前,大多数系统为以太网使用1500字节的MTU,虽然在必要时它也可设置为一个较小的值。有效载荷有时被填充(添加)数个0,以确保帧总体长度符合最小长度要求

    二、帧校验序列/循环冗余校验

    • 在以太网帧格式中,有效载荷区域之后的最后字段提供了对帧完整性的检查。循环冗余校验(CRC)字段位于尾部,有32位,有时称之为IEEE/ANSI标准的CRC32 [802.3- 2008]。要使用一个″位CRC检测数据传输 错误,被检查的消息首先需要追加″位0形成一个扩展消息。然后,扩展消息(使用 模2除洼)除以一个(乃十1)位的值,这个 作为除数的值称为生成多项式。放置在消息 的CRC字段中的值是这次除法计算中余数 的二进制反码(商被丢弃)。生成多项式已 被标准化为一系列不同的″值。以太网使用 ″=32,CRC32的生成多项式是33位的二 进制数100000100110000010001Ⅱ0110110111。 为了理解如何使用(mod 2 )二进制除法计算 余数,我们看一个CRC4的简单例子。国际 电信联盟(ITU)将CRC4的生成多项式值 标准化为10011,这是在G.704 [G704]标 准中规定的。如果我们要发送16位的消息 1001111000101111,首先开始进行下图所示的(mod2)二进制除法
    • 在该图中,我们看到这个除法的余数 是4位的值ⅡⅡ。通常,该余数的反码 (0000)将放置在帧的CRC或帧校验序列 (FCS)字段中。在接收到数据之后,接收方 执行相同的除法计算出余数,并判断该值与FCS字段的值是否匹配。如果两者不匹配,帧可能在传输过程中受损,通常被丢弃。CRC功 能可用于提示信息受损,因为位模式的任何改变极可能导致余数的改变

    三、帧大小

    • 以太网帧有最小和最大尺寸

    最小尺寸

    • 最小的帧是64字节,要求数据区(有效载荷)长度(无标签)最小为48字节
    • 当有效载荷较小时,填充字节(值为0)被添加到有效载荷尾部,以确保达到最小长度

    最大尺寸

    • 传统以太网的最大帧长度是1518字节(包括4字节CRC和14字节头部)。选择这个值 出于一种折中:如果一个帧中包括一个错误(接收到不正确的CRC校验),只需重发1.5kB 以修复该问题
    • 另一方面,MTU大小限制为1500字节。为了发送一个更大的消息,则需要多个帧(例如,对于TCP/IP网络常用的较大尺寸64KB,需要至少44个帧)
    • 由多个以太网帧构成一个更大的上层PDU的后果是,每个帧都贡献了一个固定开销(14字节的头部和4字节的CRC)。更糟的是,为了允许以太网硬件接收电路正确恢复来 自网络的数据,并为其他站提供将自已的流量与已有流量区分开的机会,以太网帧在网络中 不能无缝地压缩在一起。Ethernet Ⅱ规范除了在帧开始处定义了7字节前导和1字节SFD之 外,还指定了12字节的包间距(IPG)时间( 10Mb/s为9.6us,100Mb/s为960ns,1000Mb/s为 96ns,10000Mb/s为9.6ns)。因此, Ethernet Ⅱ的每帧效率最多为1500/(12 + 8+14+ 1500+4)= 0.975293,约98%。一种提高效率的方式是,在以太网中传输大量数据时,尽量使帧尺寸更大 一些。这可采用以太网巨型帧吓]来实现,它是一种非标准的以太网扩展(主要在1000Mb/s以 太网交换机中使用),通常允许帧尺寸高达9000字节。有些环境使用的帧称为超级巨型帧,它 们通常超过9000字节。在使用巨型帧时要谨慎,这些较大的帧无法与较小的1518字节的帧 互操作,因为它们无法由大多数传统以太网设备处理

    四、MTU和MTU路径

    • 我们从最上面的帧格式图可以看到,在很多链路层网络(例如以太网)中,携带高层协议PDU的帧大小是有限制的。以太网有效载荷的字节数通常被限制为1500,PPP通常采用相同大小以保持与以太网兼容。链路层的这种特征被称为最大传输单元(MTU)
    • 大多数的分组网络(例如以太网)都有固定的上限。大多数的流类型网络(串行链路)提供可设置的上限,它可被帧协议(例如PPP)所使用。如果IP需要发送一个数据报,并且这个数据报比链路层MTU大,则IP通过分片将数据报分解成较小的部分,使每个分片都小于MTU(我们会在后面Internet协议文章和UDP文章中讨论IP分片)
    • 路径MTU:
      • 当同一网络中的两台主机之间通信时,本地链路的MTU在会话期间对数据报大小有直接影响。当两台主机之间跨越多个网络通信时,每条链路可能有不同大小的MTU
      • 在包含所有链路的整个网络路径上,最小的MTU称为路径MTU
    • 任何两台主机之间的路径MTU不会永远不变,这取决于当时使用的路径。如果网络中的路由器或链路故障,MTU可能改变。另外,路径通常不对称(主机A到B路径可能不是 B到A的反向路径),路径MTU不需要在两个方向上相同
    • [RFCl191]规定了IPv4路径MTU发现(PMTUD)机制,[RFC1981]描述了用于IPv6 的相应机制。 [RFC4821]描述了一个补充方案,以解决这些机制中的一些间题。PMTUD用于确定某个时间的路径MTU,它在IPv6实现中是需要的。在后面的文章中,针对前面描述的ICMP和IP分片,我们将观察这个机制如何运行。我们在讨论TCP和UDP时,也会讨论它对传输性能的影响
    展开全文
  • CRC校验

    千次阅读 2012-10-05 09:22:21
    在学TCP/IP中,关于Ethernet帧结构中的最后一部分帧校验字段FCS(4B),在编程通信程序时,我们需对数据链路层通信Ethernet帧进行校验,即对帧校验字段FCS进行校验。FCS采用32位CRC校验。校验的范围包括目的地址、源...

    在学TCP/IP中,关于Ethernet帧结构中的最后一部分帧校验字段FCS(4B),在编程通信程序时,我们需对数据链路层通信Ethernet帧进行校验,即对帧校验字段FCS进行校验。FCS采用32位CRC校验。校验的范围包括目的地址、源地址字段、类型字段、数据字段。在接受段进行校验,如果发现错误,帧将被丢弃。
    下面是关于CRC的循序渐进的知识:
        循环冗余码校验(CRC=cyclic redundancy check),是一个信息字段和校验字段的长度可以任意选定的差错校验码。
        原理:任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。发过来看:x8+ x2+x1+1= 1*x8+0*x7+ 0*x6+0*x5+0*x4+1*x2+1*x1+1*1,所得到的代码是100000111。
        CRC码集选择的原则:若设码字长度为N,信息字段为K位,校验字段为R位(N=K+R),则对于CRC码集中的任一码字,存在且仅存在一个R次多项式g(x),使得V(x)=A(x)g(x)=xRm(x)+r(x);其中: m(x)为K次信息多项式, r(x)为R-1次校验多项式,g(x)称为生成多项式:g(x)=g0+g1x1+ g2x2+...+g(R-1)x(R-1)+gRxR。发送方通过指定的g(x)产生CRC码字,接收方则通过该g(x)来验证收到的CRC码字。
        CRC校验码软件生成方法:借助于多项式除法,其余数为校验字段。例如:信息字段代码为: 1011001;对应m(x)=x6+x4+x3+1 。假设生成多项式为:g(x)=x4+x3+1;则对应g(x)的代码为: 11001 。x4m(x)=x10+x8+x7+x4 对应的代码记为:10110010000;x4m(x)/ g(x)即采用多项式除法(如何运算见下段): 得余数为: 1010 (即校验字段为:1010)。发送方:发出的传输字段为: 1 0 1 1 0 0 1 1010(信息字段 校验字段)。接收方:使用相同的生成码进行校验:接收到的字段/生成码(二进制除法)如果能够除尽,则正确。
    给出余数(1010)的计算步骤:除法没有数学上的含义,而是采用计算机的模二除法,即,除数和被除数做异或运算。进行异或运算时除数和被除数最高位对齐,按位异或。
      1011001 0000
      -11001
      --------------------------
      =01111010000
      1111010000
      -11001
      -------------------------
      =0011110000
      11110000
      -11001
      --------------------------
      =00111000
      111000
      - 11001
      -------------------
      = 001010
    以下是8位CRC校验为例:
          关于CRC校验实现的方法有很多,简单起见,采用8位CRC校验,其生成多项式为G(x)= x8+ x2+x1+1。例:一个实现CRC-8的硬件电路:由8个移位寄存器和3个异或单元组成。

          计算过程如下:
        (1)开始编码解码前所有寄存器清0。
        (2)输入位作为最左边异或或操作的输入之一,8个寄存器上的移位操作同时进行,均为左移一位。
        (3)每次移位时寄存器R7的输出作为所有3个异或操作的输入之一,寄存器R0的输出作为中间异或操作的输入之一,寄存器R1的输出作为最左边异或操作的输入之一。
        (4)各个异或操作的结果作为它左边那个寄存器的输入位。
        (5)重复以上操作,每输入一位就做一次移位操作,直到输入了所有要计算的数据为止。这时,这个寄存器组中的数据就是CRC-8的结果。
        以1010为例,将1010后补8个0所得的比特序列(即101000000000)当做输入值,8个移位寄存器组中的值为00110110。该结果与101000000000除以100000111(G(x)= x8+ x2+x1+1)所得的余数相同。
        对上面计算过程分析可以得到,当寄存器R7的移出位为1时,寄存器组才和00000111进行XOR运算;移出位为0时,不做运算。每次寄存器中的数据左移后就需要从输入数据中读入一位新的数据,如果读入的新数据为1,则需要把寄存器R0置为1,然后再判断寄存器是否需要与00000111进行XOR运算。
        思路:
        //register8是一个8位的寄存器,把register8中的值置为0,在原始数据后添加8个0(因为校验字段为8位,得到的是x8 m(x))
        

    代码:
    While(数据未处理完)
    {
      if(register8首位是1)
      {
        register8中的数据左移1位;
        if(从输入中读入的新数据为1)
        {
          将register8的最低位置1;
        }
        register8= register8 XOR 00000111
      }
      else
      {
        register8中的数据左移1位;
        if(从输入中读入的新数据为1)
        {
          将register8的最低位置1;
        }
      }
    }
    
    把上面思路实现如下:
    代码:
    void checkCRC(int &chCurrByte, int chNextByte)
    {
      // CRC循环:每次调用进行8次循环,处理一个字节的数据。
      for (int nMask = 0x80; nMask > 0; nMask >>= 1)
      {
        if ((chCurrByte & 0x80) != 0)    // 首位为1:移位,并进行异或运算    
        {  
          chCurrByte <<= 1;        // 移一位
          if ( (chNextByte & nMask) != 0)  // 补一位
          {
            chCurrByte |= 1;
          }
          chCurrByte ^= 7;        // 首位已经移出,仅对低8位进行异或运算,7的二进制为0000,0111
        }
        else                // 首位为0,只移位,不进行异或运算
        {    
          chCurrByte <<= 1;        // 移一位
          if ( (chNextByte & nMask) != 0)  // 补一位
          {
            chCurrByte |= 1;
          }
        }
      }
    }
    
    CRC-32校验:
          前面介绍的串行方法,对于任意长度的生成多项式G(x)都是适用。如果发送的数据块很长的话,这种方法就不太合适,效率太低。为了提高效率,可以一次处理8,16或32位。
        Ethernet一般是对一帧数据进行CRC校验,而字节是帧的基本单位,所以最常用的CRC校验算法是按字节查表的快速算法:本字节后的CRC码,等于上一节CRC右移8位和本字节之和再与上一字节余式CRC码的低8位左移8位相加所求的CRC码。如果把8位二进制序列数的CRC(共256个)全部计算出来放在一个表中,编码时只要从表中查找对应的值进行处理即可。
       下面给出网上搜到的参数表生成程序:
    代码:
    #include <stdio.h>
        unsigned long int crc32_table[256];
        unsigned long int ulPolynomial = 0x04c11db7;
        unsigned long int Reflect(unsigned long int ref, char ch)
        { unsigned long int value(0);
              // 交换bit0和bit7,bit1和bit6,类推
              for(int i = 1; i < (ch + 1); i++)
              {  if(ref & 1)
                            value |= 1 << (ch - i);
                   ref >>= 1; }
              return value;
        }
        init_crc32_table()
        { unsigned long int crc,temp;
              // 256个值
              for(int i = 0; i <= 0xFF; i++)
              {   temp=Reflect(i, 8);
                     crc32_table[i]= temp<< 24;
                     for (int j = 0; j < 8; j++){
                   unsigned long int t1,t2;
                 unsigned long int flag=crc32_table[i]&0x80000000;
                      t1=(crc32_table[i] << 1);
                      if(flag==0)
                        t2=0;
                      else
                        t2=ulPolynomial;
                      crc32_table[i] =t1^t2 ;  }
                     crc=crc32_table[i];
                     crc32_table[i] = Reflect(crc32_table[i], 32);
              }
        }
    
    运用查表法来完成CRC-32校验码的程序:
    代码:
    unsigned   long     crc_32_tab[256]=
    {0x00000000,   0x77073096,   0xee0e612c,   0x990951ba,   0x076dc419,   0x706af48f,   0xe963a535
    ,   0x9e6495a3,0x0edb8832,…,   0x5a05df1b,   0x2d02ef8d   
      };//事先计算出的参数表,共有256项,未全部列出。   
        
      unsigned   long   GenerateCRC32(char   xdata   *   DataBuf,unsigned   long len)   
      {   
          unsigned   long   oldcrc32;   
          unsigned   long   crc32;   
          unsigned   long   oldcrc;   
          unsigned     int   charcnt;   
          char   c,t;   
          oldcrc32   =   0x00000000;   //初值为0   
          charcnt=0;   
          while(len--)
         {   
               t= (oldcrc32   >>   24)&0xFF;       //要移出的字节的值   
           oldcrc=crc_32_tab[t];                   //根据移出的字节的值查表   
           c=DataBuf[charcnt];                     //新移进来的字节值   
           oldcrc32=   (oldcrc32   <<   8)   |   c;   //将新移进来的字节值添在寄存器末字节中   
           oldcrc32=oldcrc32^oldcrc;           //将寄存器与查出的值进行xor运算   
           charcnt++;   
         }   
         crc32=oldcrc32;   
         return   crc32;   
      }   
    展开全文
  • Ethernet的解析

    2014-12-12 12:13:40
    1.按Ethernet V2.0格式封装Ethernet帧,源地址来自本机MAC地址,目的地址为随意编写的有效MAC地址,类型字段为IP协议对应值,数据字段来自文本文件(见附件),帧校验字段采用8位CRC校验。 2.输出每个帧的各字段...
  • 1.把校验字段置为0; 2.对IP头部中的每16bit进行二进制求和; 3.如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值; 4.将该16bit的值取反,存入校验字段...

    1.IP报头的checksum

    1.把校验和字段置为0;

    2.对IP头部中的每16bit进行二进制求和;

    3.如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;

    4.将该16bit的值取反,存入校验和字段。

    2.UDP/TCP报头的checksum

    UDP/TCP报头中的校验和的计算比较复杂的,要用到 UDP/TCP伪首部:先要填充伪首部各个字段,然后再将UDP/TCP报头以后(包括报头)的数据附加到伪首部的后面,再对位首部使用上述校验和计算,所得到的值才是UDP/TCP报头部分的校验和。 位首部可以用如下的结构体表示:typedef struct{
     ULONG  sourceip;    //源IP地址
     ULONG  destip;      //目的IP地址
     BYTE mbz;           //置空(0)
     BYTE ptcl;          //协议类型
     USHORT plen;        //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
    }Psd_Header; 注:1.对于UDP协议,如果不想计算的话,可以把校验和的两个字节置0,根据RFC的规定,这样上层UDP协议就不会计算校验和了2.sniffer查看校验和时,最好在中间节点。你在本机抓的时候,你的协议驱动在NDIS架构中层次太高,与标准协议栈是平级的,此时校验和处未被计算填充,还是垃圾填充数据。你在网络其它节点抓的时候,报文已经经过网卡的处理,校验和被正确设置了。

    3.参考测试代码1

    USHORT checksum(USHORT *buffer,int size)
    {
     unsigned long cksum=0;
     while(size>1)
     {
      cksum+=*buffer++;
      size-=sizeof(USHORT);
     }
     if(size)
     {
      cksum+=*(UCHAR *)buffer;
     }
     //将32位数转换成16
     while (cksum>>16)
      cksum=(cksum>>16)+(cksum & 0xffff);
     return (USHORT) (~cksum);
    }

    4.参考测试代码2

    #include "stm32f4xx.h"
    #include "usart.h"
    #include "delay.h"
    
    
    #include <string.h>
    
    #define LWIP_MAKE_U16(a, b) ((a << 8) | b) //((b << 8) | a)
    #define LWIP_hton_U16(a)  ((((a)>>8)&0x00ff) |(((a)<<8)&0xff00))
    #define LWIP_hton_U32(a) \
                             ((u32)((a>>0) & 0xff) << 24) | \
                             ((u32)((a>>8) & 0xff) << 16) | \
                             ((u32)((a>>16) & 0xff) << 8)  | \
                              (u32)((a>>24) & 0xff)
    													
    													
    #define IP4_ADDR( a,b,c,d) \
                             ((u32)((a) & 0xff) << 24) | \
                             ((u32)((b) & 0xff) << 16) | \
                             ((u32)((c) & 0xff) << 8)  | \
                              (u32)((d) & 0xff)
    
    #define FOLD_U32T(u)          (((u) >> 16) + ((u) & 0x0000ffffUL))
    #define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
    u16 lwip_standard_chksum(void *dataptr, int len)
    {
      u8 *pb = (u8 *)dataptr;
      u16 *ps, t = 0;
      u32 sum = 0;
      int odd = ((u32)pb & 1);
    
      /* Get aligned to u16_t */
      if (odd && len > 0) {
        ((u8 *)&t)[1] = *pb++;
        len--;
      }
    
      /* Add the bulk of the data */
      ps = (u16 *)(void *)pb;
      while (len > 1) {
        sum += *ps++;
        len -= 2;
      }
    
      /* Consume left-over byte, if any */
      if (len > 0) {
        ((u8 *)&t)[0] = *(u8 *)ps;
      }
    
      /* Add end bytes */
      sum += t;
    
      /* Fold 32-bit sum to 16 bits
         calling this twice is propably faster than if statements... */
      sum = FOLD_U32T(sum);
      sum = FOLD_U32T(sum);
    
      /* Swap if alignment was odd */
      if (odd) {
        sum = SWAP_BYTES_IN_WORD(sum);
      }
    
      return (u16)sum;
    }
    
    typedef struct udp_pcb {
    	//IP
    	u8 headIP;
    	u8 priority;
    	
      u16 lenIP;	
    	u16 identifyflag;
    	u16 flags ;
    	
    	u8 ttl;
    	u8 proto;
    	
    	u16 checkIP;	
      u32 srcIP;
    	u32 destIP;
    	//UDP
    	u16 srcPORT;
    	u16 destPORT;	
    	u16 lenUDP;	
    	u16 checkUDP;
    }udp_pcb_def;
    udp_pcb_def udata;
    void UDP_data(u8* retbuf,u16* retlen,u16 srcPort,u16 destPort,u8* srcIpbuf,u8* destIpbuf,u8 *dbuf,u16 dlen)
    {		
    	u32 chk_sum_IP = 0;
    	u32 chk_sum_UDP = 0;
    	u32 chk_sum = 0;
    	u16 chk_sumxxx = 0;//测试
    	
    	u16 srcPORT = srcPort;
    	u16 destPORT = destPort;
    	
    	u16 lenUDP = 8+dlen;
    	u8 proto = 0x11;//udp
    	
    	u32 srcIP = IP4_ADDR(srcIpbuf[0],srcIpbuf[1],srcIpbuf[2],srcIpbuf[3]);//高低位对调
    	u32 destIP = IP4_ADDR(destIpbuf[0],destIpbuf[1],destIpbuf[2],destIpbuf[3]);//高低位对调
    	
    	u8 * databuf = dbuf;
      u16 datalen = dlen;	
    	
    	
    	u8 headIP = 0x45;//ipv4 20B
    	u8 priority = 0x00;//优先级	
    	
    	u16 lenIP = 20+8+dlen;
    	static u16 identifyflag = 0x0001;//++
    	u16 flags = 0x0000;	
    	u8 ttl = 0x40;//生命
    	//~~~~~~~~~~~~~~~~~~~IP头
    	chk_sum=0;
      chk_sumxxx = LWIP_MAKE_U16(headIP, priority);//0x4500
    	chk_sum +=chk_sumxxx;
    	
    	chk_sumxxx = lenIP;
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = identifyflag;
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = flags;
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = LWIP_MAKE_U16(ttl, proto);//0x4011
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = (srcIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (srcIP >> 16);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (destIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (destIP >> 16);
    	chk_sum += chk_sumxxx;
    	
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = ~chk_sum;
    	chk_sum_IP=chk_sum;	
    	
    	//~~~~~~~~~~~~~~~~~~~UDP头+data
    	chk_sum=0;
    	chk_sumxxx = (srcPORT);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = (destPORT);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = (lenUDP);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = lwip_standard_chksum(databuf, datalen);//求和完的结果会 高低位对调
    	chk_sumxxx = SWAP_BYTES_IN_WORD(chk_sumxxx);
    	chk_sum += chk_sumxxx;	
    	//~~~~~~~~~~~~~~~~~~~伪UDP头
    	chk_sumxxx = (srcIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (srcIP >> 16);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (destIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = (destIP >> 16);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = (lenUDP);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = (proto);
    	chk_sum += chk_sumxxx;
    	
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = ~chk_sum;
    	
    	chk_sum_UDP=chk_sum;	
    	//~~~~~~~~~~~~~~~~~~~整理数据
    	//IP
    	udata.headIP = headIP;
    	udata.priority = priority;
    	
      udata.lenIP = LWIP_hton_U16(lenIP);
    	udata.identifyflag = LWIP_hton_U16(identifyflag);
    	udata.flags = LWIP_hton_U16(flags);
    	
    	udata.ttl = ttl;
    	udata.proto = proto;
    	
    	udata.checkIP = LWIP_hton_U16(chk_sum_IP);
      udata.srcIP = LWIP_hton_U32(srcIP);
    	udata.destIP = LWIP_hton_U32(destIP);
    	//UDP
    	udata.srcPORT = LWIP_hton_U16(srcPORT);
    	udata.destPORT = LWIP_hton_U16(destPORT);
    	udata.lenUDP = LWIP_hton_U16(lenUDP);
    	udata.checkUDP = LWIP_hton_U16(chk_sum_UDP);
    	
    	memcpy(&(retbuf[0]),(u8*)(&udata),sizeof(udp_pcb_def));
    	//retbuf = (u8*)(&udata);
    	//*retlen = sizeof(udp_pcb_def);
    	return;	
    }
    	
    
    int main(void)
    {
    	//IP_data();
    	
    	u8 ipsrcbuf[4];
    	ipsrcbuf[0]=10;
    	ipsrcbuf[1]=50;
    	ipsrcbuf[2]=133;
    	ipsrcbuf[3]=37;
    	u8 ipdesbuf[4];
    	ipdesbuf[0]=114;
    	ipdesbuf[1]=55;
    	ipdesbuf[2]=168;
    	ipdesbuf[3]=54;
    	
    	u8 sdatabuf[] = "from minh";
    	
    	u8 retbuf[(sizeof(udp_pcb_def)+sizeof(sdatabuf))];
    	u16 retlen=0;
    
    	UDP_data(retbuf,&retlen,1501,1501,ipsrcbuf,ipdesbuf,sdatabuf,sizeof(sdatabuf)-1);
    	
    	memcpy(&(retbuf[sizeof(udp_pcb_def)]),sdatabuf,sizeof(sdatabuf)-1);
    	
    }
    ^DPPPI: 1,"10.50.143.226","0.0.0.0","219.150.32.132","123.150.150.150"
    int i,n=0;
    String[] words;
    Scanner sc=new Scanner(System.in);
    System.out.println ("请输入一串数字,以逗号隔开:");
    	String str=sc.next();
    	
    	for(i=0;i<str.length();i++){
    		if(str.charAt(i)==',') n++;
    	}
    	words=str.trim().split(",");
    	for(i=0; i<str.length()-n; i++) {
    		System.out.print(words[i]+"\t");
    	}
    /*
    void IP_data(void){
    	u32 chk_sum = 0;
    	u16 chk_sumxxx = 0;
    	
    	u8 headIP1 = 0x45;//ipv4 20B
    	u8 headIP2 = 0x00;//优先级
    	
    	u16 lenIP = 0x0026;
    	u16 lenflag = 0x0001;//++
    	u16 lenflagx = 0x0000;
    	
    	u8 ttl = 0x40;//生命
      u8 proto = 0x11;//udp
    	
    	u32 srcIP = 0x0a328525;//高低位对调
    	u32 destIP = 0x7237a836;//高低位对调
    	
    	chk_sumxxx = LWIP_CHANGE_U16(LWIP_MAKE_U16(headIP1, headIP2));//0x4500
    	chk_sum +=chk_sumxxx;
    	
    	chk_sumxxx = LWIP_CHANGE_U16(lenIP);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = LWIP_CHANGE_U16(lenflag);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = LWIP_CHANGE_U16(lenflagx);
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = LWIP_CHANGE_U16(LWIP_MAKE_U16(ttl, proto));//0x4011
    	chk_sum += chk_sumxxx;
    	
    	chk_sumxxx = LWIP_CHANGE_U16(srcIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = LWIP_CHANGE_U16(srcIP >> 16);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = LWIP_CHANGE_U16(destIP & 0xFFFF);
    	chk_sum += chk_sumxxx;
    	chk_sumxxx = LWIP_CHANGE_U16(destIP >> 16);
    	chk_sum += chk_sumxxx;
    	
    	//chk_sumxxx = (chk_sum >> 16);
    	//chk_sum += chk_sumxxx;
    	//chk_sumxxx = (chk_sum & 0xFFFF);
    	//chk_sum += chk_sumxxx;
    	
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
    	chk_sum = ~chk_sum;
    }
    */
    
    
    

     

     

    展开全文
  • 以太网结构以及CRC校验

    万次阅读 2017-05-27 14:25:57
    MAC报文结构 ...以太由一个32位冗余校验码结尾。它用于检验数据传输是否出现损坏。 结构图:说一下各个字段的作用: 前同步码:第一个字段是7个字节的前同步码,1和0交替,作用是用来使接收端的适配器在接

    MAC帧报文结构

    在以太网链路上的数据包称作以太帧。以太帧起始部分由前导码和帧开始符组成。后面紧跟着一个以太网报头,以MAC地址说明目的地址源地址。帧的中部是该帧负载的包含其他协议报头的数据包(例如IP协议)。以太帧由一个32位冗余校验码结尾。它用于检验数据传输是否出现损坏。

    帧结构图:

    frame

    说一下各个字段的作用:

    • 前同步码:第一个字段是7个字节的前同步码,1和0交替,作用是用来使接收端的适配器在接收MAC帧时能够迅速调整时钟频率,使它和发送端的频率相同。
    • 帧开始定界符:第二个字段是1个字节的帧开始定界符,前六位1和0交替,最后的两个连续1表示告诉接收端适配器:“帧信息要来了,你准备接收把。
    • MAC 目的地址:第三个字段是6字节(MAC地址占48位,如:FF,FF,FF,FF,FF),发送方的网卡(MAC)地址,用处是当网卡接收到一个数据帧时,首先会检查该帧的目的地址,是否与当前适配器的物理地址相同,如果相同则会进一步处理,不同则直接丢弃。
    • 源MAC地址:发送端的MAC地址同样占6个字节
    • 类型:该字段在网络协议栈分解中及其重要,考虑当PDU(协议数据单元)来到某一层时,它需要将PDU交付给上层,而上层协议众多,所以在处理数据的时候,必须要一个字段标识我这个交付给谁。如,该字段为0x0800时,表示将有效载荷交付给IP协议,为0x0806交付给ARP,0X8035交付给RARP。
    • 数据:数据也叫有效载荷,除过当前层协议需要使用的字段外,即需要交付给上层的数据,以太网帧数据长度规定最小为46字节,最大为1500字节,如果有不到46字节时,会用填充字节填充到最小长度。最大值也叫最大传输单元(MTU),我们可以再 Linux输入 ifconfig 可以看到有一项MTU:1500。
    • 帧检验序列FCS(使用CRC校验法):检测该帧是否出现差错。

    无效的MAC帧

    当出现下列情况之一即为无效的MAC帧:

    1. 帧的长度不是8的倍数。
    2. 检验序列检验出差错。
    3. 帧长度数据字段不在46-1500之间。

    CRC校验步骤

    (1)、在发送端先把数组按照一定划分大小划分为组,假设每组K个比特,要传输的数据记位M,发送方要做的就是在数据M后面添加用于差错检测的 n 位冗余码,然后构成一个帧发送出去,也就是说此时发送的数据在原来基础上曾家了n位冗余码。

    (2)、n 位冗余码怎么来的?
    首先在原数据M后面添加n个0相当于左移n位,此时数据长度变为原来的每组K个比特加n即(k+n)位。然后用该序列除以在计算之前规定的一个长度为(n+1)位的除数P,根据二进制的模2 运算,计算出余数R。这个余数R就会作为冗余码拼接在原数据后面发送出去。

    模 2 运算:加法不进位,减法和加法一样,比如:
    1111+1010 = 0101;
    cal

    (3)、接收方把收到的每一个帧都处于同样的余数,然后检查得到的余数R:
    - 若余数R = 0,则判定这个帧没有错,接受。
    - 若余数R != 0,则判定这个帧有差错。(只能检测出该帧出现错误,无法定位出错位置)。

    注意:该检测只能保证无比特差错,而不能保证可靠传输。

    展开全文
  • 目的地址 源地址 类型 数据 帧校验序列 前导码(7字节):使接收器建立比特同步 其实定界符SFD(1字节):指示一帧的开始 目的地址DA(6字节):指出要接收该帧的工作站 源地址SA(6字节):指示发送该帧的工作站...
  • CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,...
  • CRC校验原理及步骤

    万次阅读 多人点赞 2017-06-22 11:59:11
    CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在的后面,...
  • 关于CRC校验

    2017-06-19 13:09:54
     crc即循环冗余校验码,是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在的后面...
  • HDLC 面向位协议 正常相应模式:一主 多从 异步平衡模式:点到点连接 信息帧I-frame:用户信息字段存上层 ...帧校验字段:CRC 控制字段 P/F 为1 意味轮询/终止 管理帧 00 RR 准备接受 10 RNR 不准备接受 01
  • 数据校验--CRC校验

    千次阅读 2018-12-11 16:05:27
     CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。  循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式...
  • 循环冗余校验码:是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。循 环冗余检查是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在的后面,接收设备也...
  • 3.1 数据链路层的基本概念3.1.1 数据链路层的简单模型数据链路层不关心物理层解决的问题,只关心尾和校验。​3.1.1 数据链路层的信道类型->点到点信道:这种信道使用一对一的点对点通信方式。->广播信道...

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 308
精华内容 123
关键字:

帧校验字段