当前位置: 首页 > 工具软件 > firefly_luna > 使用案例 >

Cocos2d-x与Firefly的通信

郝昊天
2023-12-01
Firefly是一个开源游戏服务器端框架,文档不全,但源码具备一定可读性。对于想用Cocos2d-x与Firefly的程序员来说,官方给的游戏 DiabloWorld 的源码是很重要的参考资源。稍加阅读源码,再结合Firefly论坛里的相关资料,可以推测出一些信息。(Hint - firefly 当前版本 v1.3.3)
  1.  客户端可以使用socket与部署Firefly的服务器通信,socket的实现不依赖于cocos2d-x的库。
  2.  客户端与服务器的通信中的数据要遵守协议规定的格式。

协议

在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 --"的字样。





 类似资料: