转自:http://hi.baidu.com/vc_net/item/513ac068b71c0035ad3e8343
本文分2部分:
第1次基础:远控视频教程中的最基本网络消息机制,虽然很简单,但是可以很好的理解消息机制
第2次软件正式代码:来源于正式软件项目,每个包带有标识符
-------------------------------第1种:介绍-------------------------------
typedef struct
{
int ID;
BYTE lparam[2048];
}COMMAND;
typedef struct
{
char FileName[MAX_PATH];//搜索到的文件夹名称#define MAX_PATH 260,此260是windows文件名最长值
int FileLen;//文件与文件夹的大小
char Time[50];//时间大小,如修改时间,访问时间等
BOOL IsDir;//判断是文件还是文件夹
BOOL Error;//如果有错误,就包含错误
HICON hIcon;//所获取的文件的一些图标,原有的是什么图标,就是什么图标
}FILEINFO;//详见问题1
整个远控中,只有这2个结构体.注意这里是不带标识符的,不过没关系,第2种解决了这种问题
-------------------------------第2种:介绍-------------------------------
前言:所谓的包,即结构体(带有标识符)
第1步:新建command文件目录--->
(1)扩展新建command.h(一些定义的值(包括10进制,16进制)头文件)------详见备注1
(2)扩展新建strPublic.h(一些公共包头,主要是协议的标识符包)--------详见备注2
(3)扩展新建strServer.h与strClient.h(即客户端与服务端的包文件)----详见备注3
备注1: ncmd = CMD_C_MACHINE_LOGIN_SYSTEM;即CMD_C_MACHINE_LOGIN_SYSTEM=0x110123;之类的定义
备注2: 公共包头
typedef struct cmdHeader{
unsigned long nReserved;
unsigned long ncmd;
unsigned long ncmdID; //指令ID
unsigned long nSerial; //指令序列
unsigned long nlen; //数据包长度
cmdHeader(){
memset(this, 0x00, sizeof(cmdHeader));
ncmdID = rand();
}
}CMD_HEADER, *PCMD_HEADER;
备注3:
--->客户端:
typedef struct c_machine_login_system{
PUB_HEADER;
char sMachineCode[32]; //机器码
unsigned long nVersion; //客户端版本号,n进制 n可选
unsigned long nReserved1; //保留字段
unsigned long nReserved2; //保留字段
c_machine_login_system(){
memset(this, 0x00, sizeof(c_machine_login_system));
ncmd = CMD_C_MACHINE_LOGIN_SYSTEM;
nlen = sizeof(c_machine_login_system)-sizeof(CMD_HEADER);
ncmdID = rand();
}
}C_MACHINE_LOGIN_SYSTEM, *PC_MACHINE_LOGIN_SYSTEM;
--->服务端:
typedef struct arep_c_machine_login{
PUB_HEADER;
unsigned long nStatus; //回复机器登录状态(可能需要公钥验证,后续处理)
unsigned long nMachineID; //机器码对应数据库唯一ID
unsigned long nMachineStatus; //终端机状态信息,0-未激活状态 1-激活状态 2-已注册状态
unsigned long nReserved1; //保留字段
unsigned long nReserved2; //保留字段
//跟包信息position_info_detail,见strPublic.h,仅当MachineStatus==2时有效
arep_c_machine_login(){
memset(this, 0x00, sizeof(arep_c_machine_login));
ncmd = CMD_AS_REP_C_MACHINE_LOGIN;
nlen = sizeof(arep_c_machine_login)-sizeof(CMD_HEADER);
ncmdID = rand();
}
}AREP_C_MACHINE_LOGIN, *PAREP_C_MACHINE_LOGIN;
第2步:统一发包规则(根据服务端的发包规则,首先必须要了解且亲自操作后才能做决定)
(1)客户端发包
C_MACHINE_LOGIN_SYSTEM cmd;
strcpy(cmd.sMachineCode,"20100904164702750199");//机器码
if(send(socket_client,(char*)&cmd,sizeof(cmd),0)==SOCKET_ERROR)
{
#ifdef _DEBUG
::OutputDebugString("发送失败:发送机器码!\n");
#endif // _DEBUG
}
(2)服务端收包
PCMD_HEADER pcm = (PCMD_HEADER)pbuf;//收到登录包的结构体后,取出其ncmd标识符
switch ( pcm->ncmd )
{
case CMD_C_MACHINE_LOGIN_SYSTEM://很明显这个pcm->ncmd,是登录包中ncmd标识符
{
onMachineLoginSystem((const PC_MACHINE_LOGIN_SYSTEM)pcm);//2次指针
}
break;
void MachineContext::onMachineLoginSystem(const PC_MACHINE_LOGIN_SYSTEM pLoginSystem)
{
arep_c_machine_login arp_c_login;//回复包的结构体
(3)服务端收包,验证数据库后,开始发包
--->成功后发包(用包的指针形式发)
//回复机器登录信息
PAREP_C_MACHINE_LOGIN pArep = (PAREP_C_MACHINE_LOGIN)bytesbufer.getDynamicData();
pArep->nStatus = STATUS_SUCCESS;
pArep->nMachineID = _machine_id;
pArep->nMachineStatus = _machine_status;
send(bytesbufer.getData(),bytesbufer.getDataSize());
--->失败后发包(用包的结构体形式发)
arep_c_machine_login arp_c_login;
arp_c_login.nStatus = STATUS_FAILED;
send((const char*)&arp_c_login,sizeof(arp_c_login));
(4)以此类推,不断循环
----------------------------------------公共头文件
command.h头文件:
#ifndef COMMAND_WL2011_H_INCLUDED
#define COMMAND_WL2011_H_INCLUDED
enum{ //所有命令字为整型32位
//客户到服务器命令字
CMD_C_MACHINE_LOGIN_SYSTEM = 0x01100001, //登录包(Client->Server)
//服务器回复客户端命令字
CMD_AS_REP_C_MACHINE_LOGIN = 0x01100002, //回复登录包(Server->Client)
};
#endif//COMMAND_WL2011_H_INCLUDED
strPublic.h头文件:
#ifndef PUBLIC_WL2011_H_INCLUDED
#define PUBLIC_WL2011_H_INCLUDED
#include <memory.h> //memset()需要用到
#include <stdlib.h> //rand()需要用到
#pragma pack(push,1) //为什么定义这个,因为sizeof引起.定义上,统一采用此种方案
typedef struct cmdHeader{
unsigned long nReserved;
unsigned long ncmd;
unsigned long ncmdID; //指令ID
unsigned long nSerial; //指令序列
unsigned long nlen; //数据包长度
cmdHeader(){
memset(this, 0x00, sizeof(cmdHeader));
ncmdID = rand();
}
}CMD_HEADER, *PCMD_HEADER;
//预定义,因为有太多的结构体包含此定义了(这样扩展也好,一改,所有结构体包统一更改),注意:\后面千万不要放//注释符之类
#define PUB_HEADER int nReserved; \
unsigned long ncmd; \
unsigned long ncmdID; \
unsigned long nSerial; \
unsigned long nlen
#pragma pack(pop) //恢复对齐状态
#endif//PUBLIC_WL2011_H_INCLUDED
strServer头文件:
#ifndef WLSERVER_WL2011_H_INCLUDED
#define WLSERVER_WL2011_H_INCLUDED
#include "strPublic.h"
#pragma pack(push,1)
typedef struct arep_c_machine_login{
PUB_HEADER;
unsigned long nStatus; //回复机器登录状态(可能需要公钥验证,后续处理)
unsigned long nMachineID; //机器码对应数据库唯一ID
unsigned long nMachineStatus; //终端机状态信息,0-未激活状态 1-激活状态 2-已注册状态
unsigned long nReserved1; //保留字段
unsigned long nReserved2; //保留字段
//跟包信息position_info_detail,见strPublic.h,仅当MachineStatus==2时有效
arep_c_machine_login(){
memset(this, 0x00, sizeof(arep_c_machine_login));
ncmd = CMD_AS_REP_C_MACHINE_LOGIN;
nlen = sizeof(arep_c_machine_login)-sizeof(CMD_HEADER);
ncmdID = rand();
}
}AREP_C_MACHINE_LOGIN, *PAREP_C_MACHINE_LOGIN;
#pragma pack(pop)
#endif//WLSERVER_WL2011_H_INCLUDED
strClient源文件:
#ifndef WLCLIENT_WL2011_H_INCLUDED
#define WLCLIENT_WL2011_H_INCLUDED
#include "strPublic.h"
#pragma pack(push,1)
typedef struct c_machine_login_system{
PUB_HEADER;
char sMachineCode[32]; //机器码
unsigned long nVersion; //客户端版本号,n进制 n可选
unsigned long nReserved1; //保留字段
unsigned long nReserved2; //保留字段
c_machine_login_system(){
memset(this, 0x00, sizeof(c_machine_login_system));
ncmd = CMD_C_MACHINE_LOGIN_SYSTEM;
// nlen = sizeof(c_machine_login_system)-sizeof(CMD_HEADER);
ncmdID = rand();
}
}C_MACHINE_LOGIN_SYSTEM, *PC_MACHINE_LOGIN_SYSTEM;
#pragma pack(pop)
#endif//WLCLIENT_WL2011_H_INCLUDED
----------------------------------------服务端代码:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
//第1步:初始化,创建,连接套接字//
WSADATA WsaData;int err;
err = WSAStartup (0x0002, &WsaData);if(err!=0) return 1; //0x0002代表版本2.0
SOCKET socket_bind=socket(AF_INET,SOCK_STREAM,0);
if(socket_bind==INVALID_SOCKET)
{
#ifdef _DEBUG
::OutputDebugString("创建套接字错误!\n");
#endif // _DEBUG
return 1;
}
SOCKADDR_IN serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
serveraddr.sin_port=htons(55551);
bind(socket_bind,(SOCKADDR*)&serveraddr,sizeof(SOCKADDR));
listen(socket_bind,50);
SOCKET client;
SOCKADDR_IN saRemote;int nRemoteLen = sizeof(saRemote);
if((client=accept(socket_bind,(sockaddr*)&saRemote, &nRemoteLen))!=INVALID_SOCKET )
{
while(1)
{
char m_RecvBuffer[4096];
int m_nRecvedSize=sizeof(m_RecvBuffer);
PCMD_HEADER pcm = (PCMD_HEADER)m_RecvBuffer;
if(recv(client,(char*)&m_RecvBuffer,m_nRecvedSize,0)==SOCKET_ERROR)
{
AfxMessageBox("接收失败,退出重recv接收!");
break;
}
else
{
switch ( pcm->ncmd )
{
case CMD_C_MACHINE_LOGIN_SYSTEM://很明显这个pcm->ncmd,是登录包中ncmd标识符
{
printf("收到登录包(Client->Server)!\n");
PC_MACHINE_LOGIN_SYSTEM pcmd=(PC_MACHINE_LOGIN_SYSTEM)pcm;
printf("收到的机器码是:%s\n",pcmd->sMachineCode);
}
break;
}
}
}
}
closesocket(client);
WSACleanup();
return 0;
}
----------------------------------------客户端代码:
DWORD WINAPI Connect(LPVOID lpParam);
void CMyDlg::OnBnClickedButtonConnect()
{
CreateThread(0,0,Connect,this,0,0);
}
SOCKET socket_client;
DWORD WINAPI Connect(LPVOID lpParam)
{
//第1步:初始化,创建,连接套接字//
WSADATA WsaData;int err;
err = WSAStartup (0x0002, &WsaData);if(err!=0) return 1; //0x0002代表版本2.0
socket_client=socket(AF_INET,SOCK_STREAM,0);
if(socket_client==INVALID_SOCKET){AfxMessageBox("创建套接字错误!\n");return 1;}
SOCKADDR_IN sconnect_pass;
sconnect_pass.sin_family=AF_INET;
sconnect_pass.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
sconnect_pass.sin_port=htons(55551);
if(SOCKET_ERROR==connect(socket_client,(SOCKADDR*)&sconnect_pass,sizeof(SOCKADDR))) {printf("连接服务端失败!\n");return 1;}
else
{
while(1)
{
char m_RecvBuffer[4096];
int m_nRecvedSize=sizeof(m_RecvBuffer);
PCMD_HEADER pcm = (PCMD_HEADER)m_RecvBuffer;
if(recv(socket_client,(char*)&m_RecvBuffer,m_nRecvedSize,0)==SOCKET_ERROR)
{
AfxMessageBox("接收失败,退出重recv接收!");
break;
}
else
{
switch ( pcm->ncmd )
{
case CMD_AS_REP_C_MACHINE_LOGIN://很明显这个pcm->ncmd,是登录包中ncmd标识符
{
AfxMessageBox("收到登录包(Client->Server)!");
}
break;
}
}
}
}
closesocket(socket_client);
WSACleanup();
return 0;
}
void CMyDlg::OnBnClickedButtonRun()
{/*
PC_MACHINE_LOGIN_SYSTEM cmd;
strcpy(cmd->sMachineCode,"20100904164702750199");//机器码
cmd->
CMD_HEADER cmd1;
// cmd1.
if(send(socket_client,(char*)&cmd,sizeof(cmd),0)==SOCKET_ERROR)
{
#ifdef _DEBUG
::OutputDebugString("发送失败:发送机器码!\n");
#endif // _DEBUG
}
*/
//发包
C_MACHINE_LOGIN_SYSTEM cmd;
strcpy(cmd.sMachineCode,"20100904164702750199");//机器码
CString str;
str.Format("%d",cmd.nVersion);
// cmd.nVersion
// CMD_HEADER cmd1;
// cmd1.
if(send(socket_client,(char*)&cmd,sizeof(cmd),0)==SOCKET_ERROR)
{
#ifdef _DEBUG
::OutputDebugString("发送失败:发送机器码!\n");
#endif // _DEBUG
}
}
附注1:第2种项目正式收发包规则举例代码,已上传至邮箱,即取即用
附注2:
//预定义,因为有太多的结构体包含此定义了(这样扩展也好,一改,所有结构体包统一更改)
//提醒:千万别在" nReserved; \"的后面使用注释符//,否则会报错!
#define PUB_HEADER int nReserved; \
unsigned long ncmd; \
unsigned long ncmdID; \
unsigned long nSerial; \
unsigned long nlen
它是属于结构体的成员,但是只有通过指针才能访问到.毕竟它在结构体内是#define形式,
另外结构体内的构造函数初始化了一些东西,起主要作用的是 ncmd = CMD_C_MACHINE_LOGIN_SYSTEM;表示标识符
其实很好理解:同第1种一样.ncmd对ncmd,即发包方有个ncmd,那么收包也有个对应的ncmd
附注3:
附加包含目录:上1层用../command,上2层用../../command.注意:选择的时候一定要选择整个工程(对应整个工程),而不是选择单个文件(对应单个文件),因为它们属性是不同的.
按需所写了,也许有的文件不需要附加包含此目录
至此,原理已讲完了,目前只做了登录包介绍,后期随需求扩展