最近要在单片机上实现一个TCP/IP协议,遇到一个怪问题我的电脑发出的TCP包,没带数据时(三握手),用这段代码能校验成功...
volatile U16 crc;
volatile U16 length, temp; RxdNetBuff.IpFrame.ttl = 0;//伪首部0字段
temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长
//length=(RxdNetBuff.IpFrame.VerandIphLen&0x0f)<<2;//IP首部长度  
length=20 ;//不考虑IP首部选项,直接作为20字节计算
RxdNetBuff.IpFrame.Crc =htons(temp-length);//IP包CRC位置转换为伪首的TCP包数据长度
crc = CheckSum(&RxdNetBuff.IpPacket.IpPacket[4], temp-8);//从IP包第8个字节开始计算
UartPrintfCh1("VerifyTcpCrc:temp;%u,length:%u crc:%u\r\n",temp,length,crc);当发出带有数据的包时,得到的CRC就不为0了...
更奇怪的是,我用一个sniffer软件叫Iris截取发出的数据包时,他居然也说我电脑网卡发出的带数据的TCP校验和错误,不带数据的正确
但我用另外一台电脑测试能收到这个包,又说明这个包是没问题的...
我现在糊涂了...

解决方案 »

  1.   

    http://jianjian.blog.51cto.com/35031/4932http://topic.csdn.net/t/20031109/17/2443012.htmlhttp://topic.csdn.net/t/20050614/15/4081732.html提供几个链接,希望可以帮上忙。
      

  2.   

    单片机上为什么要自己实现TCP等协议,太麻烦了吧为什么不用 lwip 等?
    这是TCP较验和计算相关的一些代码:/* TCP 包头 */
    typedef struct tcp_header
    {
    u_16 sport; // 16位源端口号
    u_16 dport; // 16位目地端口号
    u_32 serial; // 32位序号
    u_32 serial_2; // 32位确认序号
    u_16 multi; // 4 位首部长度 保留6位 URG ACK PSH PST SYN FIN
    u_16 win_size; // 16位窗口大小
    u_16 crc; // 16位TCP较验和
    u_16 urgent; // 16位紧急指针
    }tcp_header,* ptcp_header;/* IP 伪首部 */
    typedef struct psd_header
    {
    u_32 saddr; //源地址
    u_32 daddr; //目的地址
    u_char mbz; //置空
    u_8 protocol; //协议类型
    u_16 tcpl; //TCP长度
    }psd_header;/* 计算 TCP 头较验值 */
    u_16 tcp_set_checksum(
     u_16 *buffer, //从TCP头开始
     u_16 size , //TCP头长度+数据长度
     in_addr ia_src_addr , //源地址
     in_addr ia_dst_addr , //目的地址
     u_8 c_protocol //应用协议类型
    )
    {
    tcp_header *tcphead = (tcp_header*)( buffer);
    u_32 checksum = 0;
    u_16 *p = NULL;
    psd_header tcpsd_header = { 0 }; //填充 IP 伪首部
    tcpsd_header.saddr = ia_src_addr.s_addr;
    tcpsd_header.daddr = ia_dst_addr.s_addr;
    tcpsd_header.protocol = c_protocol;
    tcpsd_header.tcpl = htons( size );
    tcphead->crc = 0; p = (u_16*)&tcpsd_header;
    checksum += *p++;
    checksum += *p++;
    checksum += *p++;
    checksum += *p++;
    checksum += *p++;
    checksum += *p++; p = (u_16*)tcphead; while( size > 1 )
    {
    checksum += *p++;
    size -= 2;
    }
    if( size > 0 )
    {
    checksum += *(u_char*)p;
    } checksum = (checksum>>16) + (checksum&0xFFFF);
    checksum += (checksum>>16);
    tcphead->crc = (u_16)(~checksum);
    return tcphead->crc;
    }
      

  3.   

    我最近也在做TCP方面的研究,TCP checksum的数据是TCP伪头和TCP头和数据组成的
    TCP伪头就是源IP(4字节),目标IP(4字节),协议htons(0x0006),TCP数据长度(4字节)
    计算checksum时,TCP头里的checksum默认为0x0000
    我用的函数是
    USHORT checksum(USHORT* buffer,int size)   
    {   
    unsigned long cksum=0;   
    while(size>1)   
    {   
    cksum+=*buffer++;   
    size-=sizeof(USHORT);     
    } if(size)   
    {   
    cksum+=*(UCHAR*)buffer;     
    }   
    cksum=(cksum>>16)+(cksum&0xffff);   
    cksum+=(cksum>>16);      return (USHORT)(~cksum);     
    }  假设我要计算一个包
    #pragma pack(push) // 将当前pack设置压栈保存
    #pragma pack(1)typedef struct Packet
    {
    unsigned char  DestMAC[6];
    unsigned char  SourMAC[6];
    unsigned short EthType;
    unsigned char  IPVersion;
    unsigned char  IPType;
    unsigned short Len1;
    unsigned short IDCode;
    unsigned char  IPFlags;
    unsigned char  Fragment_Offest;
    unsigned char  Time_to_alive;
    unsigned char  Protocol;
    unsigned short IP_Checksum;
    unsigned long  SourIP;
    unsigned long  DestIP;
    unsigned short SourPort;
    unsigned short DestPort;
    unsigned long  Sn;
    unsigned long  An;
    unsigned char  Data_Offest;
    unsigned char  flags;
    unsigned short window;
    unsigned short cheksum;
    unsigned short u_point;
    unsigned char  padding[16];

    }PACKET, *PPACKET;
    #pragma pack(pop) // 恢复先前的pack设置
    PACKET buf;
    buf.cheksum=htons(0);UCHAR tempbuf[100];
    memset(tempbuf,0,100);
    memcpy(tempbuf,(UCHAR*)&buf+0x1a,44); //从SourIP开始复制
    tempbuf[45]=36; //长度
    tempbuf[47]=6; //协议
    buf.cheksum=checksum((USHORT*)tempbuf,48);
      

  4.   

        volatile U16 crc;
        volatile U16 length, temp;    RxdNetBuff.IpFrame.ttl = 0;//伪首部0字段
        temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长这里的temp是一个network byte order
        //length=(RxdNetBuff.IpFrame.VerandIphLen&0x0f)<<2;//IP首部长度  
        length=20 ;//不考虑IP首部选项,直接作为20字节计算这里的Length是一个Host byte order
        RxdNetBuff.IpFrame.Crc =htons(temp-length/*一个network byte order减去一个Host byte order,结果是什么东西?*/);//IP包CRC位置转换为伪首的TCP包数据长度
        crc = CheckSum(&RxdNetBuff.IpPacket.IpPacket[4], temp-8);//从IP包第8个字节开始计算
        UartPrintfCh1("VerifyTcpCrc:temp;%u,length:%u crc:%u\r\n",temp,length,crc);
      

  5.   

    to 楼上两位
    temp = htons(RxdNetBuff.IpFrame.TotalLen); //IP包总长这里的temp是一个network byte order 
    ---------------------------------
    这是网卡取出来的数据,因此要换一下顺序才能得到IP包真正的长度,所以要经过 htons 才转成主机顺序
    这个htons是我写的一个可逆宏...换过来换过去是一样的,别看字面意思请大家帮我考虑下,为什么我的代码对不带数据的包能校验成功,
    带数据的包则错误.那个软件也这么说
    原因肯定出在这个长度上...IP包总长度-IP首部长度 == TCP包长度
    TCP包长度 + 伪首部12字节 == 需要校验数据总长 
    伪首部里存的长度 == TCP包总长  还是 TCP首部长度 ? 查了很多资料都没有结果
     
      

  6.   

    我没有使用另外的伪首部结构是因为IP包头这时候已经没有用了,
    就把他的TTL当作伪首0值
    CRC当作长度,协议类型与两IP地址不变,
    正好连在一起12字节
      

  7.   

    这是网卡取出来的数据,因此要换一下顺序才能得到IP包真正的长度,所以要经过 htons 才转成主机顺序 
    =========
    谁告诉你htons是转换成主机字节序的?
      

  8.   

    楼上你的咋死脑筋呢把 AA BB 转成 BB AA 函数
    也能把 BB AA 转成 AA BB啊,
    都说这是个可逆的过程了,不要看字面意思
    你一定要改成ntohs吧那加条宏就是
    #define ntohs(x) htons(x)
      

  9.   

    LZ。这个传输是怎么传的(单片机的没搞过)
    如果是像普通那样,是把TCP分片成若干IP小包进行传输,在路由上还会验证如果是这样。
    我没有使用另外的伪首部结构是因为IP包头这时候已经没有用了,
    就把他的TTL当作伪首0值
    CRC当作长度,协议类型与两IP地址不变,
    正好连在一起12字节 
    这段就有问题了
      

  10.   

    倒!TCP头校验这么简单的计算,还给这么多分有着功夫,仔细复习一下TCP/IP详解不就啥都有了~
      

  11.   

    Q 5.14: Why am I seeing lots of packets with incorrect TCP checksums?
    A: If the packets that have incorrect TCP checksums are all being sent
    by the machine on which Ethereal is running, this is probably because
    the network interface on which you're capturing does TCP checksum
    offloading. That means that the TCP checksum is added to the packet by
    the network interface, not by the OS's TCP/IP stack; when capturing on
    an interface, packets being sent by the host on which you're capturing
    are directly handed to the capture interface by the OS, which means
    that they are handed to the capture interface without a TCP checksum
    being added to them.
    看来12楼的兄弟思路对了,
    可能当包有数据时,网卡或者路由器已经将包重新弄过了,检验和也重新计算过
    但这种包怎么检验,仍然没解决..
    实在没办法只好是个TCP就收下,不抛掉检验和不对的包了to 11楼
    TCP算检验和用的伪首根本不真正发送,所以抓包软件也抓不到,
    再说抓包软件也只能抓到操作系统TCP栈内的数据,抓不到网卡真正发出的数据
    再说谁做这个,电脑里不安几个sniffer软件的to 13楼,
    我桌子上就摆着一本<TCP/IP详解 卷1 协议>
    请帮我翻一下哪页讲了伪首内长度字节的真正含义站着说话不腰痛的,你们的回复说明你们很不熟悉TCP的检验和,请看清我的帖子标题再进来,谢谢
    另外http://topic.csdn.net/t/20050614/15/4081732.html 
    的14楼也提到这个同样的问题,已经PM请教了
      

  12.   

    TCP 是上层协议,应该不依赖下层协议(下层是IP,IPX...都是可以的),但它又非常想知道下层协议的一些信息(比如源、目的IP等),所以不能和IP头一起算较验值,只好找个 IP伪首部一起算了
    个人理解啊,别当真!
      

  13.   

    Checksum:  16 bits    The checksum field is the 16 bit one's complement of the one's
        complement sum of all 16 bit words in the header and text.  If a
        segment contains an odd number of header and text octets to be
        checksummed, the last octet is padded on the right with zeros to
        form a 16 bit word for checksum purposes.  The pad is not
        transmitted as part of the segment.  While computing the checksum,
        the checksum field itself is replaced with zeros.    The checksum also covers a 96 bit pseudo header conceptually
        prefixed to the TCP header.  This pseudo header contains the Source
        Address, the Destination Address, the Protocol, and TCP length.
        This gives the TCP protection against misrouted segments.  This
        information is carried in the Internet Protocol and is transferred
        across the TCP/Network interface in the arguments or results of
        calls by the TCP on the IP.                     +--------+--------+--------+--------+
                         |           Source Address          |
                         +--------+--------+--------+--------+
                         |         Destination Address       |
                         +--------+--------+--------+--------+
                         |  zero  |  PTCL  |    TCP Length   |
                         +--------+--------+--------+--------+ ===> The TCP Length is the TCP header length plus the data length in
          octets (this is not an explicitly transmitted quantity, but is
          computed), and it does not count the 12 octets of the pseudo
          header.See RFC793