一个简单的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;
}
////////////////////////////////////////////////////////////////////////////////////////
时间: 2024-12-20 13:32:32

一个简单的SOCKET程序的数据包结构和封解包函数的相关文章

一个简单的socket程序运行与抓包查看

为了熟悉socket编程,在ubuntu下运行了一个现有例子(Networking and Socket programming tutorial in C - CodeProject),并通过抓包查看运行结果. 步骤一.编译 gcc server.c -o server.out gcc client.c -o client.out 步骤二.打开wireshark 步骤三.运行 ./server.out ./client.out 步骤四.查看wireshark抓包结果,如下图所示. 由于wire

Linux网络编程(一):一个简单的socket程序

服务器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define SER_PORT 8000 #define MAX_LINE 1024 int main(int argc, char **argv) { int listenfd

创建一个简单的银行程序包.........未完善,待续

2练习1:创建一个简单的银行程序包   练习目标-Java 语言中面向对象的封装性及构造器的使用. 任务 在这个练习里,创建一个简单版本的(账户类)Account类.将这个源文件放入banking程序包中.在创建单个帐户的默认程序包中,已编写了一个测试程序TestBanking.这个测试程序初始化帐户余额,并可执行几种简单的事物处理.最后,该测试程序显示该帐户的最终余额. 1. 创建banking 包 2. 在banking 包下创建Account类.该类必须实现上述UML框图中的模型. 声明一

创建一个简单的银行程序包

创建一个简单的银行程序包   练习目标-Java 语言中面向对象的封装性及构造器的使用. 任务 在这个练习里,创建一个简单版本的(账户类)Account类.将这个源文件放入banking程序包中.在创建单个帐户的默认程序包中,已编写了一个测试程序TestBanking.这个测试程序初始化帐户余额,并可执行几种简单的事物处理.最后,该测试程序显示该帐户的最终余额. 1. 创建banking 包 2. 在banking 包下创建Account类.该类必须实现上述UML框图中的模型. 声明一个私有对象

Windows socket之最简单的socket程序

原文:Windows socket之最简单的socket程序 最简单的服务器的socket程序流程如下(面向连接的TCP连接 ): 1. WSAStartup(); 初始化网络库的使用. 2. socket(); 获得一个socket. 3. bind(); 把获得的socket绑定到一个ip 和端口.既然作为服务器, ip通常为本地IP127.0.0.1. 4. listen(); 监听已经绑定了指定端口的socket. 5. accept(); 接受一个来自客户端的连接. accept()返

一个简单的Java程序

一个.NET技术还是很菜的水平的猿人现在要去学习Java不知道是坏是好,无从得知啊! 不过在网上看了好多Java方面的简单例子,感觉Java还是蛮不错的么!不管以后怎么样啦,先开始自己的Java菜鸟之旅吧! 建立一个Java项目,建立一个属于自己的包,然后就开始自己的Java之旅... 创建的时候勾上这个生成main方法的选项,这个好像类似与我们.Net程序里控制台程序有木有.... 创建完成后就是这么一个样子,可以看到我们的包,还有给我们创建好自己的类,并且带了一个静态的main方法咋看就像.

一个简单的flask程序

初始化 所有Flask程序都必须创建一个程序实例. 程序实例是Flask类的对象,经常使用下述代码创建: from flask import Flask app = Flask(__name__) Flask类的构造函数只有一个必须指定的参数,及程序主模块或包的名字. 在大多数程序中,Python的__name__变量就是所需的值. 路由和视图函数 客户端把请求发送给Web服务器,Web服务器再把请求发送给Flask程序实例. 程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个URL到

Linux系统学习笔记之 1 一个简单的shell程序

不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步:创建一个包含命令和控制结构的文件 第二步:修改这个文件的权限使它可以执行. 使用chmod u+x 第三步:执行shell sh /test/example.sh Shell变量 变量:是shell传递数据的一种方法,用来代表每个取值的符号名 shell有两类变量:临时变量和永久变量 临时变量是sh

《Linux内核分析》MOOC课程 反汇编一个简单的C程序,分析汇编代码

一个简单c程序 分析一个简单的c程序 main.c 如下图: 用命令 gcc –S –o main.s main.c -m32编译成汇编文件.在汇编文件中有许多的虚指令并不会形成机器指令,为了使分析简单我们把大部分去掉: 得到如下图所示: 栈的介绍 APUE中指出每一个c程序,都有一个独立的地址空间,在内存中的典型布局如下: 对栈的操作和我们在数据结构中的栈的操作是类似的,ebp,esp(具体名称与cpu体系结构相关) 这两个寄存器直接与栈的操作相关. 栈地址是从高到低的方向分配的. 开始一个新