将客户端的功能,注册给lua脚本使用。
主要功能有:
1、创建客户端
2、连接服务端
3、运行客户端
4、写入字节流
5、读取字节流
--------------------------------------------------
简易客户端EasyTcpClient.hpp
#ifndef _EasyTcpClient_hpp_ #define _EasyTcpClient_hpp_ #ifdef _WIN32 #include <windows.h> #include <WinSock2.h> #else #include<unistd.h> #include<arpa/inet.h> #include<string.h> #define SOCKET int #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR (-1) #endif #include <stdio.h> #include "DataHeader.hpp" #include "CELLMsgStream.hpp" class EasyTcpClient { public: EasyTcpClient() { _sock = INVALID_SOCKET; } //虚析构函数 virtual ~EasyTcpClient() { Close(); } //初始化socket void InitSocket() { #ifdef _WIN32 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif // _WIN32 if (_sock != INVALID_SOCKET) { Close(); } _sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == _sock) { printf("错误,建立Socket失败...\n"); } else { printf("建立Socket成功...\n"); } } int Connect(const char* ip, unsigned short port) { if (INVALID_SOCKET == _sock) { InitSocket(); } sockaddr_in _sin = {}; _sin.sin_family = AF_INET; _sin.sin_port = htons(port); #ifdef _WIN32 _sin.sin_addr.S_un.S_addr = inet_addr(ip); #else _sin.sin_addr.s_addr = inet_addr(ip); #endif int ret = connect(_sock, (const sockaddr*)&_sin, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("错误,连接服务端失败...\n"); } else { printf("连接服务端成功...\n"); } return ret; } bool isRun() { return (_sock != INVALID_SOCKET); } bool onRun() { if (isRun()) { fd_set fdRead; FD_ZERO(&fdRead); FD_SET(_sock, &fdRead); timeval t = {1, 0}; int ret = select(_sock + 1, &fdRead, 0, 0, &t); if (ret < 0) { Close(); printf("select任务结束1。\n"); return false; } if ( FD_ISSET(_sock, &fdRead) ) { if (-1 == RecvData()) { Close(); printf("select任务结束2。\n"); return false; } } } } //接收数据 处理粘包 拆分包 int RecvData() { char szRecv[1024] = {}; int nLen = recv(_sock, szRecv, sizeof(DataHeader), 0); if (nLen <= 0) { printf("服务端%d已退出。\n", _sock); return -1; } DataHeader* pHeader = (DataHeader*)szRecv; recv(_sock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); onNetMsg(pHeader); return 0; } virtual void onNetMsg(DataHeader* pData) { switch (pData->cmd) { case CMD_LOGIN_RET: { //LoginResult *pLogin = (LoginResult*)pData; //printf("login result:%d\n", pLogin->ret); } break; case CMD_LOGOUT_RET: { CELLReadStream r(pData); //读取消息长度 r.ReadInt16(); //读取消息命令 r.getNetCmd(); auto n1 = r.ReadInt8(); auto n2 = r.ReadInt16(); auto n3 = r.ReadInt32(); auto n4 = r.ReadFloat(); auto n5 = r.ReadDouble(); uint32_t n = 0; r.onlyRead(n); char name[32] = {}; auto n6 = r.ReadArray(name, 32); char pw[32] = {}; auto n7 = r.ReadArray(pw, 32); int ata[10] = {}; auto n8 = r.ReadArray(ata, 10); printf("recv msg, name:%s pw:%s\n", name, pw); } break; case CMD_NEW_USER_JOIN: { printf("new user join.\n"); } break; } } //发送数据 int SendData(DataHeader* pHeader) { if (isRun() && pHeader) { return send(_sock, (const char*)pHeader, pHeader->dataLength, 0); } return SOCKET_ERROR; } int SendData(const char* pData, int len) { if (isRun() && pData) { return send(_sock, pData, len, 0); } return SOCKET_ERROR; } void Close() { if (_sock != INVALID_SOCKET) { #ifdef _WIN32 closesocket(_sock); //清除Windows socket环境 WSACleanup(); #else close(_sock); #endif _sock = INVALID_SOCKET; } } private: SOCKET _sock; }; #endif
cocos2d-x-3.16\tests\lua-empty-test\project\Classes\lua_CppNet100_register.hpp
lua中的函数,注册到客户端中,在消息处理中,回调处理消息。
cocos2d::LUA_FUNCTION _callBack
class NativeClient :public EasyTcpClient { private: cocos2d::LUA_FUNCTION _callBack = 0; public: void setCallBack(cocos2d::LUA_FUNCTION cb) { _callBack = cb; } void onNetMsg(DataHeader* pData) { if (_callBack) { //执行lua的函数 LuaEngine* engine = LuaEngine::getInstance(); lua_State* L = engine->getLuaStack()->getLuaState(); //给lua传递参数 tolua_pushuserdata(L, pData); tolua_pushnumber(L, pData->dataLength); //执行lua的函数 engine->getLuaStack()->executeFunctionByHandler(_callBack, 2);//指定lua函数的参数为2个 } } };
创建客户端、连接服务端方法,注册到lua中
static void* CellClient_Create(cocos2d::LUA_FUNCTION cb, int sendSize=1024, int recvSize=1024) { NativeClient* obj = new NativeClient(); obj->setCallBack(cb); obj->InitSocket(); return obj; } //给lua调用的,创建客户端 int lua_CellClient_Create(lua_State* L) { //获取lua的函数 cocos2d::LUA_FUNCTION cb = toluafix_ref_function(L, 1, 0); void* obj = CellClient_Create(cb); tolua_pushuserdata(L, obj); return 1; } static bool CellClient_Connect(NativeClient* obj, const char* ip, unsigned short port) { return obj->Connect(ip, port); } //给lua调用的,连接服务器 int lua_CellClient_Connect(lua_State* L) { //获取lua的参数 //客户端 NativeClient* obj = (NativeClient*)tolua_touserdata(L, 1, nullptr); //服务端ip地址 const char* ip = tolua_tostring(L, 2,nullptr); //服务端端口号 unsigned short port = 0; luaval_to_ushort(L, 3, &port); bool b = CellClient_Connect(obj, ip, port); tolua_pushboolean(L, b); return 1; }
字节流的方法,举例:
//创建读取字节流 static void* CELLReadStream_Create(void* data) { CELLReadStream* obj = new CELLReadStream((DataHeader*)data); return obj; } int lua_CellClient_ReadStream_Create(lua_State* L) { void* data = tolua_touserdata(L, 1, nullptr); void* obj = CELLReadStream_Create(data); tolua_pushuserdata(L, obj); return 1; } //读取字符串 int lua_CellClient_ReadStream_ReadString(lua_State* L) { auto obj = (CELLReadStream*)tolua_touserdata(L, 1, nullptr); std::string str; obj->ReadString(str); tolua_pushstring(L, str.c_str()); return 1; } static void* CELLWriteStream_Create(int nSize) { CELLWriteStream* obj = new CELLWriteStream(nSize); return obj; } int lua_CELLWriteStream_Create(lua_State* L) { int nSize = lua_tointeger(L, 1); void* obj = CELLWriteStream_Create(nSize); tolua_pushuserdata(L, obj); return 1; } static bool CELLWriteStream_WriteInt8(CELLWriteStream* obj, int8_t n) { if (obj) return obj->Write(n); return false; } int lua_CellClient_WriteStream_WriteInt8(lua_State* L) { CELLWriteStream* ws = (CELLWriteStream*)tolua_touserdata(L, 1, nullptr); int8_t n = (int8_t)tolua_tonumber(L, 2, 0); bool b = CELLWriteStream_WriteInt8(ws, n); tolua_pushboolean(L, b); return 1; }
注册到lua中
//将函数注册到lua中 int lua_CppNet100_register(lua_State* L) { tolua_open(L); tolua_module(L, nullptr, 0); tolua_beginmodule(L, nullptr); lua_register(L, "ClientCreate", lua_CellClient_Create);//创建客户端,注册回调函数 lua_register(L, "ClientConnect", lua_CellClient_Connect);//连接服务器 lua_register(L, "WriteStream_Create", lua_CELLWriteStream_Create);//创建写入字节流 lua_register(L, "WriteStream_WriteInt8", lua_CellClient_WriteStream_WriteInt8);//字节流写入Int8 lua_register(L, "ReadStream_Create", lua_CellClient_ReadStream_Create);//创建读取字节流 lua_register(L, "ReadStream_ReadString", lua_CellClient_ReadStream_ReadString);//字节流读取 tolua_endmodule(L); return 0; }
在lua中使用
//回调函数 local function callback(data, len) cclog("callback_onNetMsg:len="..len) local rs = ReadStream_Create(data)//创建读取字节流 -- 读取消息长度 ReadStream_ReadInt16(rs) -- 读取消息类型 ReadStream_ReadUInt16(rs) ReadStream_ReadInt8(rs) ReadStream_ReadInt16(rs) ReadStream_ReadInt32(rs) ReadStream_ReadFloat(rs) ReadStream_ReadDouble(rs) cclog(ReadStream_ReadString(rs)) cclog(ReadStream_ReadString(rs)) local len = ReadStream_ReadUInt32(rs) for n=1,len do ReadStream_ReadInt32(rs) end end //创建客户端 local _client = ClientCreate(callback) ClientConnect(_client, "127.0.0.1", 4567); //创建写入字节流 local ws = WriteStream_Create(128) WriteStream_WriteUInt16(ws, 2)-- 消息类型 WriteStream_WriteInt8(ws, 1) WriteStream_WriteInt16(ws, 2) WriteStream_WriteInt32(ws, 3) WriteStream_WriteFloat(ws, 4.5) WriteStream_WriteDouble(ws, 6.7) WriteStream_WriteString(ws, "usename01") WriteStream_WriteString(ws, "password") -- --写数组长度 WriteStream_WriteUInt32(ws, 5) --写数组元素 WriteStream_WriteInt32(ws, 1) WriteStream_WriteInt32(ws, 2) WriteStream_WriteInt32(ws, 3) WriteStream_WriteInt32(ws, 4) WriteStream_WriteInt32(ws, 5) -- every frame local function tick() -- ClientSendData(_client, "yyy", 3) SendWriteStream(_client, ws) ClientOnRun(_client) end