在Firefly框架中,net节点负责管理连接(包括连接建立和连接断开的处理)和数据的接收与发送的处理。firefly.netconnect.protoc模块定义了登陆服务器协议,而客户端与服务器交互的数据格式在firefly.netconnect.datapack模块中声明。基本格式如下:
HEAD_0 |
HEAD_1 |
HEAD_2 |
HEAD_3 |
protoVersion |
serverVersion |
length |
commandId |
data (消息体) |
各个字段的意义在模块中均有说明。在使用Firefly框架的时候,需要使客户端中数据的协议信息与服务器中相关的协议信息一致。在服务器端,可以直接修改firefly.netconnect.datapack中DataPackProtoc的初始值。
在不修改Firefly源码的情况下,默认HEAD_0等字段都是0。DiabloWorld 客户端源码包含了处理通信的代码,抛开多线程与消息队列的设计,可以抽取出简短的可以通信的代码。
bool test() {
int dwServerIP = inet_addr("192.168.1.128");
unsigned short wPort = 1000;
int m_hSocket = -1;
WSADATA wsd;
WSAStartup(MAKEWORD(2,2), &wsd);
m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
if (m_hSocket == -1) {
return false;
}
sockaddr_in SocketAddr;
memset(&SocketAddr,0,sizeof(SocketAddr));
SocketAddr.sin_family=AF_INET;
SocketAddr.sin_port=htons(wPort);
SocketAddr.sin_addr.s_addr=dwServerIP;
memset(&(SocketAddr.sin_zero),0,sizeof(SocketAddr.sin_zero));
int iErrorCode = 0;
iErrorCode = connect(m_hSocket,(sockaddr*)&SocketAddr,sizeof(SocketAddr));
if (iErrorCode == -1) {
printf("socket connect error:%d\n",errno);
return false;
}
Message* msg = new Message("Hello, World", 1);
// send data
int ret = send(m_hSocket, msg->data, 23, 0);
if (ret == -1) {
printf("fail to send data\n", errno);
}
return true;
}
其中 Message 的定义如下:
class Message {
public:
char HEAD_0;
char HEAD_1;
char HEAD_2;
char HEAD_3;
char protoVersion;
byte serverVersion[4];
byte length[4];
byte commandId[4];
// message body
char* data;
Message();
Message(const char* data, int commandId);
int datalength();
~Message();
};
Message::Message():data(NULL) { }
Message::Message(const char* data, int commandId) {
HEAD_0 = 1;
HEAD_1 = 0;
HEAD_2 = 0;
HEAD_3 = 0;
protoVersion = 0;
int a = 0;
serverVersion[3] = (byte)(0xff&a);
serverVersion[2]=(byte)((0xff00&a)>>8);
serverVersion[1]=(byte)((0xff0000&a)>>16);
serverVersion[0]=(byte)((0xff000000&a)>>24);
int b=strlen(data)+4;
length[3]=(byte)(0xff&b);
length[2]=(byte)((0xff00&b)>>8);
length[1]=(byte)((0xff0000&b)>>16);
length[0]=(byte)((0xff000000&b)>>24);
int c = commandId;
this->commandId[3]=(byte)(0xff&c);;
this->commandId[2]=(byte)((0xff00&c)>>8);
this->commandId[1]=(byte)((0xff0000&c)>>16);
this->commandId[0]=(byte)((0xff000000&c)>>24);
printf("%d", datalength());
this->data = new char[datalength()];
memcpy(this->data+0,&HEAD_0,1);
memcpy(this->data+1,&HEAD_1,1);
memcpy(this->data+2,&HEAD_2,1);
memcpy(this->data+3,&HEAD_3,1);
memcpy(this->data+4,&protoVersion,1);
memcpy(this->data+5,&serverVersion,4);
memcpy(this->data+9,&length,4);
memcpy(this->data+13,&this->commandId,4);
memcpy(this->data+17,data,strlen(data));
}
Message::~Message() {
SAFE_DELETE_ARRAY(data);
}
int Message::datalength() {
return bytesToInt(length) + 13;
}
这里bytesToInt方法定义如下:
int bytesToInt(byte* bytes)
{
int addr = bytes[3] & 0xFF;
addr |= ((bytes[2] << 8) & 0xFF00);
addr |= ((bytes[1] << 16) & 0xFF0000);
addr |= ((bytes[0] << 24) & 0xFF000000);
return addr;
}
以上代码实现客户端连接服务器,并且向服务器发送数据的过程。如果服务器端解析数据失败,那么就会打印"illegal data package --"的字样。