一个简单的SOCKET程序的数据包结构和封解包函数
/*练习写套接字通信程序时候写的一段代码,本来想写个聊天室但写来写去进度卡在界面上接节下来都是通信部分的代码 因为只是试验用所以都是用C写的,等界面部分完工后会用类来封装一下 因为本人E文很烂所以变量和函数的命名是具有中国特色的,求理解.不过我注释的很详细了 谨以此文纪念我那坑爹的编程自学生涯......................**/ #include "stdio.h" #include <windows.h> //////////////////////////////数据包接构////////////////////////////////////// //数据包类型CTOS为客户端使用的数据包,STOC为服务端使用的数据包 #define CTOS 1 #define STOC 2 //数据包存储管道每个包最大为2000字节,其中数据载荷为最大1800字节,其余留着扩展数据包头 struct SJGD { BYTE sjgd[2000]; //数据缓冲区 DWORD sjcd; //数据长度 }; //数据包头大小为8字节 struct MSG_TOU { DWORD lxid; //数据包类型 DWORD sxid; //数据包顺序标号 DWORD sjbcd; //数据包长度 }; //服务端-》客户端包 struct MSG_STOC { DWORD cmd; //命令标识 DWORD sjbcd; //整个MSG_STOC数据包缓冲区的长度 BYTE shuju[1000]; //附加数据缓冲区 }; //客户端-》服务器包 struct MSG_CTOS { DWORD cmd; //命令标识 DWORD sjbcd; //整个MSG_CTOS数据包缓冲区的长度 BYTE shuju[1800]; //附加数据缓冲区 }; //共用体 union MSG_DATA { struct MSG_STOC msg_stoc; struct MSG_CTOS msg_ctos; }; //完整数据包 struct SJB { struct MSG_TOU tou; //数据包头 union MSG_DATA data; //数据缓冲区 }; ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// //封包函数第一个参数为数据包类型,第二个为命令标识,这是数据包的重点远控命令会转换成数字在这里传输 //第三个为数据包顺序标号,第四个参数为附加数据缓冲区,第五个参数为打包数据缓冲区 //作用是把数据填充成一个标准的远控数据包为下面的发包做准备 int Fengbao(DWORD lxid,DWORD cmd,DWORD sxid,struct SJGD*sjgd,struct SJB*sjb) { sjb->tou.lxid=lxid; //填充数据包类型 sjb->tou.sxid=sxid; //填充数据包顺序标号 if(lxid==CTOS) { sjb->data.msg_ctos.cmd=cmd; //填充命令标识 MoveMemory(sjb->data.msg_ctos.shuju,sjgd->sjgd,sjgd->sjcd); //填充附加数据 sjb->data.msg_ctos.sjbcd=sjgd->sjcd; //填充整个附加数据缓冲区的长度 } if(lxid==STOC) { sjb->data.msg_stoc.cmd=cmd; //填充命令标识 MoveMemory(sjb->data.msg_stoc.shuju,sjgd->sjgd,sjgd->sjcd); //填充附加数据 sjb->data.msg_stoc.sjbcd=sjgd->sjcd; //填充整个附加数据缓冲区的长度 } sjb->tou.sjbcd=20+sjgd->sjcd; //填充数据包长度 if((lxid!=CTOS)&&(lxid!=STOC)) return 0; return 1; } /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// //发包函数第一个参数为待发数据包,第二个参数为数据管道缓冲区 //作用是把一个标准数据包以二进制的形式发送到数据管道缓冲区 int Fabao(struct SJB*sjb,struct SJGD*sjgd) { MoveMemory(sjgd->sjgd,&sjb->tou.lxid,4); //填充数据包类型 MoveMemory(sjgd->sjgd+4,&sjb->tou.sxid,4);//填充数据包顺序标号 if(sjb->tou.lxid==CTOS) { MoveMemory(sjgd->sjgd+8,&sjb->tou.sjbcd,4); //填充数据包长度 MoveMemory(sjgd->sjgd+12,&sjb->data.msg_ctos.cmd,4);//填充命令标识 MoveMemory(sjgd->sjgd+16,&sjb->data.msg_ctos.sjbcd,4);//填充整个附加数据缓冲区的长度 MoveMemory(sjgd->sjgd+20,sjb->data.msg_ctos.shuju,sjb->data.msg_ctos.sjbcd); //填充附加数据 sjgd->sjcd=sjb->tou.sjbcd; //更新数据管道长度数据 return 1; } if(sjb->tou.lxid==STOC) { MoveMemory(sjgd->sjgd+8,&sjb->tou.sjbcd,4);//填充数据包长度 MoveMemory(sjgd->sjgd+12,&sjb->data.msg_stoc.cmd,4);//填充命令标识 MoveMemory(sjgd->sjgd+16,&sjb->data.msg_stoc.sjbcd,4);//填充整个附加数据缓冲区的长度 MoveMemory(sjgd->sjgd+20,sjb->data.msg_ctos.shuju,sjb->data.msg_stoc.sjbcd); //填充附加数据 sjgd->sjcd=sjb->tou.sjbcd; //更新数据管道长度数据 return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// //函数作用是直接从内存读取一个4字节的整数,为下面的解包做准备 int hex_int(char c) //从内存读取一个1字节的整数 { if((c>='A')&&(c<='F')) { return (int)(c-'A'+10); } if((c>='0')&&(c<='9')) { return (int)(c-'0'); } return 0; } DWORD hex_dw(BYTE*hex) //从内存读取一个4节的整数 { DWORD D=0; char x[2]; sprintf(x,"%.2X",hex[0]); D+=hex_int(x[1]); D+=hex_int(x[0])*16; sprintf(x,"%.2X",hex[1]); D+=hex_int(x[1])*16*16; D+=hex_int(x[0])*16*16*16; sprintf(x,"%.2X",hex[2]); D+=hex_int(x[1])*16*16*16*16; D+=hex_int(x[0])*16*16*16*16*16; sprintf(x,"%.2X",hex[3]); D+=hex_int(x[1])*16*16*16*16*16*16; D+=hex_int(x[0])*16*16*16*16*16*16*16; return D; } /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// //解包函数第一个参数是待解包的数据管道缓冲区,第二个参数为解包数据存储结构 //函数作用是把数据管道中的数据解封为一个标准的远控数据包,用以控制程序流程 int Jiebao(struct SJGD*sjgd,struct SJB*sjb) { sjb->tou.lxid=hex_dw(sjgd->sjgd); //解封数据包类型 sjb->tou.sxid=hex_dw(sjgd->sjgd+4); //解封数据包顺序标号 sjb->tou.sjbcd=hex_dw(sjgd->sjgd+8); //解封数据包长度 if(sjb->tou.lxid==CTOS) { sjb->data.msg_ctos.cmd=hex_dw(sjgd->sjgd+12); //解封命令标识 sjb->data.msg_ctos.sjbcd=hex_dw(sjgd->sjgd+16); //解封整个附加数据缓冲区的长度 MoveMemory(sjb->data.msg_ctos.shuju,sjgd->sjgd+20,sjb->data.msg_ctos.sjbcd); //解封附加数据 return 1; } if(sjb->tou.lxid==STOC) { sjb->data.msg_stoc.cmd=hex_dw(sjgd->sjgd+12); //解封命令标识 sjb->data.msg_stoc.sjbcd=hex_dw(sjgd->sjgd+16); //解封整个附加数据缓冲区的长度 MoveMemory(sjb->data.msg_stoc.shuju,sjgd->sjgd+20,sjb->data.msg_ctos.sjbcd); //解封附加数据 return 1; } return 0; } ////////////////////////////////////////////////////////////////////////////////////////
醉过海阔天空,且狂且痴且醉趁年少