UDP 校检和和算法

#include <Winsock2.h> #include <stdio.h> #define IP_HDRINCL 2 // Header is included with data. typedef struct _IPHEADER { UCHAR VerHeadLen; //版本号和头长度 UCHAR TOS; //服务类型 USHORT Length; //IP包总长度 USHORT ID; //IP包唯一标识 USHORT Flags; //标志 UCHAR TTL; //生存时间 UCHAR Protocol; //协议 USHORT Checksum; //校验和 ULONG SourceIP; //源IP地址 ULONG DestIP; //目标IP }IPHEADER; typedef struct _UDPHEADER { USHORT SourcePort; //源端口 USHORT DestPort; //目的端口 USHORT Length; //包长度 USHORT CheckSum; //校验和 }UDPHEADER; USHORT checksum(USHORT * buff, int size) { ULONG cksum = 0; while (size > 1) { cksum += *(buff++); size -= sizeof(USHORT); } if(size) { cksum += *((UCHAR*)buff); } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return (USHORT)(~cksum); } void UdpHeaderChecksum(IPHEADER *IpHeader,UDPHEADER *UdpHeader,char * payload,int payloadlen) { char buf[1024]; char *ptr = buf; int chksumlen = 0; ULONG zero = 0; //包含源IP地址和目的IP地址 memcpy(ptr,&IpHeader->SourceIP,sizeof(IpHeader->SourceIP)); ptr += sizeof(IpHeader->SourceIP); chksumlen += sizeof(IpHeader->SourceIP); memcpy(ptr,&IpHeader->DestIP,sizeof(IpHeader->DestIP)); ptr += sizeof(IpHeader->DestIP); chksumlen += sizeof(IpHeader->DestIP); //8位0域 memcpy(ptr,&zero,1); ptr += 1; chksumlen += 1; //协议 memcpy(ptr,&IpHeader->Protocol,sizeof(IpHeader->Protocol)); ptr += sizeof(IpHeader->Protocol); chksumlen += sizeof(IpHeader->Protocol); //UDP长度 memcpy(ptr,&UdpHeader->Length,sizeof(UdpHeader->Length)); ptr += sizeof(UdpHeader->Length); chksumlen += sizeof(UdpHeader->Length); //UDP端口号 memcpy(ptr,&UdpHeader->SourcePort,sizeof(UdpHeader->SourcePort)); ptr += sizeof(UdpHeader->SourcePort); chksumlen += sizeof(UdpHeader->SourcePort); memcpy(ptr,&UdpHeader->DestPort,sizeof(UdpHeader->DestPort)); ptr += sizeof(UdpHeader->DestPort); chksumlen += sizeof(UdpHeader->DestPort); //UDP长度 memcpy(ptr,&UdpHeader->Length,sizeof(UdpHeader->Length)); ptr += sizeof(UdpHeader->Length); chksumlen += sizeof(UdpHeader->Length); //16位UDP校验和 memcpy(ptr,&zero,sizeof(UdpHeader->CheckSum)); ptr += sizeof(USHORT); chksumlen += sizeof(USHORT); //净荷 memcpy(ptr,payload,payloadlen); ptr += payloadlen; chksumlen += payloadlen; //补齐到下一个16位边界 for(int i = 0; i < payloadlen %2; i++) { *ptr = 0; ptr++; chksumlen++; } //计算校验和,填充到UDP头 UdpHeader->CheckSum = checksum((USHORT*)buf,chksumlen); } int main() { int i = 0; WSADATA wsaData; int wsaret = WSAStartup(0x101,&wsaData); if(wsaret != 0) { printf("WSAStartup失败\n"); } char szDestIP[] = "192.168.12.110"; char szSourceIP[] = "192.168.12.169"; USHORT nDestPort = 1234; USHORT nSourcePort = 8888; char szMsg[] = "123456"; int nMsgLen = strlen(szMsg); //创建原始套接字 SOCKET sRaw = ::socket(AF_INET,SOCK_RAW,IPPROTO_UDP); //有效IP头包含选项 BOOL bIncl = TRUE; i = ::setsockopt(sRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bIncl,sizeof(bIncl)); char buff[1024] = {0}; int TotalLength = sizeof(IPHEADER) + sizeof(UDPHEADER) + nMsgLen; //IP头 IPHEADER * IpHeader = (IPHEADER*)buff; IpHeader->VerHeadLen = (4 << 4 | (sizeof(_IPHEADER)/sizeof(ULONG))); IpHeader->Length = TotalLength; IpHeader->TTL = 128; IpHeader->Protocol = IPPROTO_UDP; IpHeader->SourceIP = ::inet_addr(szSourceIP); IpHeader->DestIP = ::inet_addr(szDestIP); IpHeader->Checksum = checksum((USHORT*)IpHeader,sizeof(IPHEADER)); //UDP头 UDPHEADER * UdpHeader = (UDPHEADER*)(buff + sizeof(IPHEADER)); UdpHeader->SourcePort = htons(nSourcePort); UdpHeader->DestPort = htons(nDestPort); UdpHeader->Length = htons(sizeof(UDPHEADER) + nMsgLen); UdpHeader->CheckSum = 0; UdpHeaderChecksum(IpHeader,UdpHeader,szMsg,nMsgLen); char* pData = buff + sizeof(IPHEADER) + sizeof(UDPHEADER); memcpy(pData,szMsg,nMsgLen); //设置目的地址 SOCKADDR_IN Addr = {0}; Addr.sin_family = AF_INET; Addr.sin_port = htons(nDestPort); memset(Addr.sin_zero,0,8); Addr.sin_addr.S_un.S_addr = ::inet_addr(szDestIP); //发送原始UDP封包 int nRet = 0; for(i = 0; i < 5; i++) { nRet = sendto(sRaw,buff,TotalLength,0,(sockaddr*)&Addr,sizeof(sockaddr)); if(nRet == SOCKET_ERROR) { printf("sendto() faild:%d\n",WSAGetLastError()); break; } else { printf("send %d bytes\n",nRet); } } closesocket(sRaw); return 0; } 刚开始时,服务程序根本接收不到我发的包,用ethereal抓包时发现数据确实是发送出去了,但显示信息表示UDP校验和不正确。 后来发现计算UDP校验和时,我把IP头也计算进去,实际上,UDP的校验和只需要计算从UDP头开始到后面的净荷。 找到问题后觉得十分简单,但如果有些朋友也犯了和我一样的错误的话,希望有所帮助

上一篇:HDU 1281——棋盘游戏——————【最大匹配、枚举删点、邻接表方式】


下一篇:.NET中低版本程序调用高版本DLL