cocos2dx 3.x 中 Lua socket 和 node.js 利用scoket互相通信读写二进制数据
2014-11-14 16:25
881 查看
第一部分,Lua socket如何读写二进制数据。
cocos2dx 3.x 版本已经集成了lua socket所以可以直接使用无需自己集成。首先需要初始化lua socket 如下:
这里connect的两个参数就是,链接地址和端口号。settimeout设置为0 是让等待数据的时候不需要阻塞,这里我们使用lua并没有加入线程的支持所以lua是单线程。如果不设置为非阻塞,那么在等待socket数据的时候冻结lua的执行能力。这样scoket就连接上,等待着数据的读和写,我们这里读和写都使用同一个socket对象。
那么,socket如何得知有数据需要读和写呢? 如下:
我们看到,select的方法放入我们connect返回的tcp对象,会返回reads 和 writes 就是可读和可写的表。具体select的参数和返回值参看lua socket API。reads和writes表的长度说明了是否有数据需要读写。这个段代码需要定时检测以确保一旦有数据就可以被及时的处理。
接下来就是如何进行数据的读写了。lua没有读写二进制数据的方法,所以我们需要引入一个扩展lpack.c,是c实现的lua二进制数据打包解包的功能。但是我找到了一个用lua 翻译这个c版本的库。如下。
那么借助这个库我们可以这么做:
读取数据我们使用lua socket的receive方法截取数据,以后再用解包函数解包,以后再强转成我们需要的类型。读取数据直接把数据按照类型打包,send出去即可。
第二部分,node.js的数据读写。
node.js 我只是使用了原生的socket API 并没有使用任何框架。封装了一个数据读写的模块如下:
读写数据只是利用node.js提供的Buff对象打包了数据以后用socket进行操作清晰明了。
这是服务器启动的代码,关键在入on data的回调函数,我们利用系统提供的buff和socket对象,构建我们封装的BuffRead和BuffWrite就可以进行数据的读写了。
cocos2dx 3.x 版本已经集成了lua socket所以可以直接使用无需自己集成。首先需要初始化lua socket 如下:
socket = require("socket"); tcp = socket.connect("127.0.0.1", 1024); -- non-blocking tcp:settimeout(0);
这里connect的两个参数就是,链接地址和端口号。settimeout设置为0 是让等待数据的时候不需要阻塞,这里我们使用lua并没有加入线程的支持所以lua是单线程。如果不设置为非阻塞,那么在等待socket数据的时候冻结lua的执行能力。这样scoket就连接上,等待着数据的读和写,我们这里读和写都使用同一个socket对象。
那么,socket如何得知有数据需要读和写呢? 如下:
-- check readable and writable local reads, writes = socket.select({tcp}, {tcp}, 0); if #reads == 1 then -- data can read end if #request > 0 and #writes == 1 then -- data can write end
我们看到,select的方法放入我们connect返回的tcp对象,会返回reads 和 writes 就是可读和可写的表。具体select的参数和返回值参看lua socket API。reads和writes表的长度说明了是否有数据需要读写。这个段代码需要定时检测以确保一旦有数据就可以被及时的处理。
接下来就是如何进行数据的读写了。lua没有读写二进制数据的方法,所以我们需要引入一个扩展lpack.c,是c实现的lua二进制数据打包解包的功能。但是我找到了一个用lua 翻译这个c版本的库。如下。
-- lpack.c -- a Lua library for packing and unpacking binary data -- Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> -- 29 Jun 2007 19:27:20 -- This code is hereby placed in the public domain. -- with contributions from Ignacio Castaño <castanyo@yahoo.es> and -- Roberto Ierusalimschy <roberto@inf.puc-rio.br>. -- Conversion from C to lua by Angelo Yazar, 2013. local ffi = require "ffi"; local bit = require "bit"; local C = ffi.C; local tonumber = tonumber; local string = string; local assert = assert; ffi.cdef [[ int isdigit( int ch ); ]] local OP_ZSTRING = 'z'; --/* zero-terminated string */ local OP_BSTRING = 'p'; --/* string preceded by length byte */ local OP_WSTRING = 'P'; --/* string preceded by length word */ local OP_SSTRING = 'a'; --/* string preceded by length size_t */ local OP_STRING = 'A'; --/* string */ local OP_FLOAT = 'f'; --/* float */ local OP_DOUBLE = 'd'; --/* double */ local OP_NUMBER = 'n'; --/* Lua number */ local OP_CHAR = 'c'; --/* char */ local OP_BYTE = 'b'; --/* byte = unsigned char */ local OP_SHORT = 'h'; --/* short */ local OP_USHORT = 'H'; --/* unsigned short */ local OP_INT = 'i'; --/* int */ local OP_UINT = 'I'; --/* unsigned int */ local OP_LONG = 'l'; --/* long */ local OP_ULONG = 'L'; --/* unsigned long */ local OP_LITTLEENDIAN = '<'; --/* little endian */ local OP_BIGENDIAN = '>'; --/* big endian */ local OP_NATIVE = '='; --/* native endian */ local OP_NONE = function() end; function badcode(c) assert(false, "bad character code: '" .. tostring(c) .. "'"); end local function isLittleEndian() local x = ffi.new("short[1]", 0x1001); local e = tonumber(( ffi.new("char[1]", x[0]) )[0]); if e == 1 then return true; end return false; end function doendian(c) local e = isLittleEndian(); if c == OP_LITTLEENDIAN then return not e; elseif c == OP_BIGENDIAN then return e; elseif c == OP_NATIVE then return false; end return false; end function doswap(swap, a, T) if T == "byte" or T == "char" then return a; end if swap then -- if T == "double" or T == "float" then -- this part makes me unhappy -- a = ffi.new(T .. "[1]", a); local m = ffi.sizeof(T); local str = ffi.string(a, m):reverse(); ffi.copy(a, str, m); return tonumber(a[0]); --else -- return bit.bswap( a ) --end end return a; end function isdigit(c) return C.isdigit(string.byte(c)) == 1; end function l_unpack(s, f, init) local len = #s; local i = (init or 1); local n = 1; local N = 0; local cur = OP_NONE; local swap = false; --lua_pushnil(L); local values = {} local function push(value) values = value; n = n + 1; end local function done() return i, unpack(values); end local endianOp = function(c) swap = doendian(c); -- N = 0 -- I don't think this is needed end local stringOp = function(c) if i + N - 1 > len then return done; end push(s:sub(i, i + N - 1)); i = i + N; N = 0; end local zstringOp = function(c) local l = 0; if i >= len then return done; end local substr = s:sub(i); l = substr:find('\0'); push(substr:sub(0, l)); i = i + l; end function unpackNumber(T) return function() local m = ffi.sizeof(T) ; if i + m - 1 > len then return done; end local a = ffi.new(T.."[1]"); ffi.copy(a, s:sub(i,i+m), m); push(doswap(swap, tonumber(a[0]), T)); i = i + m; end end function unpackString(T) return function() local m = ffi.sizeof(T); if i + m > len then return done; end local l = ffi.new(T .. "[1]"); ffi.copy(l, s:sub(i), m); l = doswap(swap, tonumber(l[0]), T); if i + m + l - 1 > len then return done; end i = i + m; push(s:sub(i, i + l - 1)); i = i + l; end end local unpack_ops = { [OP_LITTLEENDIAN] = endianOp, [OP_BIGENDIAN] = endianOp, [OP_NATIVE] = endianOp, [OP_ZSTRING] = zstringOp, [OP_STRING] = stringOp, [OP_BSTRING] = unpackString("unsigned char"), [OP_WSTRING] = unpackString("unsigned short"), [OP_SSTRING] = unpackString("size_t"), [OP_NUMBER] = unpackNumber("double"), [OP_DOUBLE] = unpackNumber("double"), [OP_FLOAT] = unpackNumber("float"), [OP_CHAR] = unpackNumber("char"), [OP_BYTE] = unpackNumber("unsigned char"), [OP_SHORT] = unpackNumber("short"), [OP_USHORT] = unpackNumber("unsigned short"), [OP_INT] = unpackNumber("int"), [OP_UINT] = unpackNumber("unsigned int"), [OP_LONG] = unpackNumber("long"), [OP_ULONG] = unpackNumber("unsigned long"), [OP_NONE] = OP_NONE, [' '] = OP_NONE, [','] = OP_NONE, } for c in (f .. '\0'):gmatch('.') do if not isdigit(c) then if cur == OP_STRING then if N == 0 then push(""); elseif stringOp(cur) == done then return done(); end else if N == 0 then N = 1; end for k = 1, N do if unpack_ops[cur] then if unpack_ops[cur](cur) == done then return done(); end else badcode(cur); end end end cur = c; N = 0; else N = 10 * N + tonumber(c); end end return done(); end function l_pack(f, ...) local args = {f, ...}; local i = 1; local N = 0; local swap = false; local b = ""; local cur = OP_NONE; local pop = function() i = i + 1; return args[i]; end local endianOp = function(c) swap = doendian(c); -- N = 0 -- I don't think this is needed end local stringOp = function(c) b = b .. pop(); if c == OP_ZSTRING then b = b .. '\0'; end end function packNumber(T) return function() local a = pop() a = doswap(swap, a, T); a = ffi.new(T .. "[1]", a); b = b .. ffi.string(a, ffi.sizeof(T)); end end function packString(T) return function() local a = pop(); local l = #a; local ll = doswap(swap, l, T); ll = ffi.new(T .. "[1]", ll); b = b .. ffi.string(ll, ffi.sizeof(T)); b = b .. a; end end local pack_ops = { [OP_LITTLEENDIAN] = endianOp, [OP_BIGENDIAN] = endianOp, [OP_NATIVE] = endianOp, [OP_ZSTRING] = stringOp, [OP_STRING] = stringOp, [OP_BSTRING] = packString("unsigned char"), [OP_WSTRING] = packString("unsigned short"), [OP_SSTRING] = packString("size_t"), [OP_NUMBER] = packNumber("double"), [OP_DOUBLE] = packNumber("double"), [OP_FLOAT] = packNumber("float"), [OP_CHAR] = packNumber("char"), [OP_BYTE] = packNumber("unsigned char"), [OP_SHORT] = packNumber("short"), [OP_USHORT] = packNumber("unsigned short"), [OP_INT] = packNumber("int"), [OP_UINT] = packNumber("unsigned int"), [OP_LONG] = packNumber("long"), [OP_ULONG] = packNumber("unsigned long"), [OP_NONE] = OP_NONE, [' '] = OP_NONE, [','] = OP_NONE, } for c in (f .. '\0'):gmatch('.') do if not isdigit(c) then if N == 0 then N = 1; end for k = 1, N do if pack_ops[cur] then pack_ops[cur](cur); else badcode(cur); end end cur = c; N = 0; else N = 10 * N + tonumber(c); end end return b; end string.pack = l_pack; string.unpack = l_unpack;
那么借助这个库我们可以这么做:
function Socket.readInt8() local next, val = string.unpack(tcp:receive(1), "b") return tonumber(val); end function Socket.readInt16() local next, val = string.unpack(tcp:receive(2), "h"); return tonumber(val); end function Socket.readInt32() local next, val = string.unpack(tcp:receive(4), "i"); return tonumber(val); end -- Server string data must end of "\n" function Socket.readString() return tostring(tcp:receive()); end -- fmt: one or more letter Codes string -- A : string -- c : char -- b : byte (unsigned char) -- h : short -- H : unsigned short -- i : int -- I : unsigned int -- l : long -- L : unsigned long function Socket.send(fmt, ...) tcp:send(string.pack(fmt, ...)); end
读取数据我们使用lua socket的receive方法截取数据,以后再用解包函数解包,以后再强转成我们需要的类型。读取数据直接把数据按照类型打包,send出去即可。
第二部分,node.js的数据读写。
node.js 我只是使用了原生的socket API 并没有使用任何框架。封装了一个数据读写的模块如下:
var BufferRead = function(buff) { var offset = 0; return { readInt8: function() { var int8 = buff.readInt8(offset); offset += 1; return int8; }, readInt16: function() { var int16 = buff.readInt16LE(offset); offset += 2; return int16; }, readInt32: function() { var int32 = buff.readInt32LE(offset); offset += 4; return int32; }, readString: function(len) { var str = buff.toString("utf8", offset, offset + len); offset += len; return str; } }; } var BufferWrite = function(socket) { return { writeInt8: function(int8) { var buff = new Buffer(1); buff.writeInt8(int8, 0); socket.write(buff); }, writeInt16: function(int16) { var buff = new Buffer(2); buff.writeInt16LE(int16, 0); socket.write(buff); }, writeInt32: function(int32) { var buff = new Buffer(4); buff.writeInt32LE(int32, 0); socket.write(buff); }, writeString: function(str) { socket.write(str); }, /** * fmt is format string * A : string * b : byte (unsigned char) * h : short * i : int */ write: function(fmt) { for (var i = 0; i < fmt.length; i++) { switch (fmt.charAt(i)) { case 'A': this.writeString(arguments[i + 1]); break; case 'b': this.writeInt8(arguments[i + 1]); break; case 'h': this.writeInt16(arguments[i + 1]); break; case 'i': this.writeInt32(arguments[i + 1]); break; } } } }; } module.exports = { BufferRead: BufferRead, BufferWrite: BufferWrite };
读写数据只是利用node.js提供的Buff对象打包了数据以后用socket进行操作清晰明了。
var protocal = require("./Protocol.js"); var net = require("net"); var buffData = require("./BufferData.js"); var server = net.createServer(); server.listen(1024, function() { console.log('Server start local host at port 1024'); }); server.on("connection", function(socket) { console.log("server socket connected"); socket.on('data', function(buff) { var buffRead = new buffData.BufferRead(buff); var buffWrite = new buffData.BufferWrite(socket); var reqCode = buffRead.readInt32(); protocal.handlers[reqCode](socket, buffRead, buffWrite); socket.pipe(socket); }); socket.on('end', function() { console.log('server socket disconnected'); socket.destroy(); }); socket.on('error', function(error) { console.log("Client error: %s", error.toString()); socket.destroy(); }); }); server.on("error", function (error) { console.log("Server error code = %s", error.toString()); }); server.on("close", function() { console.log("Server closed"); });
这是服务器启动的代码,关键在入on data的回调函数,我们利用系统提供的buff和socket对象,构建我们封装的BuffRead和BuffWrite就可以进行数据的读写了。
相关文章推荐
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- Node.js中使用Buffer编码、解码二进制数据详解
- Node.js中使用Buffer编码、解码二进制数据详解
- Node.js权威指南 (7) - 实现基于TCP与UDP的数据通信
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- cocos2dx lua Node节点 私有数据存取
- Node利用buffer操作,编码,解码二进制数据
- Node.js实现基于TCP与UDP的数据通信
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- 基于socket.io和node.js搭建即时通信系统
- 使用socket.io和node.js搭建及时通信系统
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- Node.js+socket.io实现实时通信
- Node.js, 使用 Buffers 操作,编码、解码,二进制数据。
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- 函数项目【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)函数项目
- cocos2dx 3.x getStringFromFile lua 读取二进制文件
- 在js中利用Microsoft.XMLDOM进行二进制与base64互相转换代码
- 用node.js(socket.io)实现数据实时推送