cocos2dx 3.x 中 Lua socket 和 node.js 利用scoket互相通信读写二进制数据

前端之家收集整理的这篇文章主要介绍了cocos2dx 3.x 中 Lua socket 和 node.js 利用scoket互相通信读写二进制数据前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

第一部分,Lua socket如何读写二进制数据。


cocos2dx 3.x 版本已经集成了lua socket所以可以直接使用无需自己集成。首先需要初始化lua socket 如下:


  1. socket = require("socket");
  2. tcp = socket.connect("127.0.0.1",1024);
  3. -- non-blocking
  4. tcp:settimeout(0);

这里connect的两个参数就是,链接地址和端口号。settimeout设置为0 是让等待数据的时候不需要阻塞,这里我们使用lua并没有加入线程的支持所以lua是单线程。如果不设置为非阻塞,那么在等待socket数据的时候冻结lua的执行能力。这样scoket就连接上,等待着数据的读和写,我们这里读和写都使用同一个socket对象。



那么,socket如何得知有数据需要读和写呢? 如下:

  1. -- check readable and writable
  2. local reads,writes = socket.select({tcp},{tcp},0);
  3. if #reads == 1 then
  4. -- data can read
  5. end
  6. if #request > 0 and #writes == 1 then
  7. -- data can write
  8. end

我们看到,select的方法放入我们connect返回的tcp对象,会返回reads 和 writes 就是可读和可写的表。具体select的参数和返回值参看lua socket API。reads和writes表的长度说明了是否有数据需要读写。这个段代码需要定时检测以确保一旦有数据就可以被及时的处理。



接下来就是如何进行数据的读写了。lua没有读写二进制数据的方法,所以我们需要引入一个扩展lpack.c,是c实现的lua二进制数据打包解包的功能。但是我找到了一个用lua 翻译这个c版本的库。如下。

  1. -- lpack.c
  2. -- a Lua library for packing and unpacking binary data
  3. -- Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
  4. -- 29 Jun 2007 19:27:20
  5. -- This code is hereby placed in the public domain.
  6. -- with contributions from Ignacio Castaño <castanyo@yahoo.es> and
  7. -- Roberto Ierusalimschy <roberto@inf.puc-rio.br>.
  8.  
  9. -- Conversion from C to lua by Angelo Yazar,2013.
  10.  
  11.  
  12. local ffi = require "ffi";
  13. local bit = require "bit";
  14. local C = ffi.C;
  15. local tonumber = tonumber;
  16. local string = string;
  17. local assert = assert;
  18.  
  19. ffi.cdef [[
  20. int isdigit( int ch );
  21. ]]
  22.  
  23. local OP_ZSTRING = 'z'; --/* zero-terminated string */
  24. local OP_BSTRING = 'p'; --/* string preceded by length byte */
  25. local OP_WSTRING = 'P'; --/* string preceded by length word */
  26. local OP_SSTRING = 'a'; --/* string preceded by length size_t */
  27. local OP_STRING = 'A'; --/* string */
  28. local OP_FLOAT = 'f'; --/* float */
  29. local OP_DOUBLE = 'd'; --/* double */
  30. local OP_NUMBER = 'n'; --/* Lua number */
  31. local OP_CHAR = 'c'; --/* char */
  32. local OP_BYTE = 'b'; --/* byte = unsigned char */
  33. local OP_SHORT = 'h'; --/* short */
  34. local OP_USHORT = 'H'; --/* unsigned short */
  35. local OP_INT = 'i'; --/* int */
  36. local OP_UINT = 'I'; --/* unsigned int */
  37. local OP_LONG = 'l'; --/* long */
  38. local OP_ULONG = 'L'; --/* unsigned long */
  39. local OP_LITTLEENDIAN = '<'; --/* little endian */
  40. local OP_BIGENDIAN = '>'; --/* big endian */
  41. local OP_NATIVE = '='; --/* native endian */
  42. local OP_NONE = function() end;
  43.  
  44. function badcode(c)
  45. assert(false,"bad character code: '" .. tostring(c) .. "'");
  46. end
  47.  
  48. local function isLittleEndian()
  49. local x = ffi.new("short[1]",0x1001);
  50. local e = tonumber(( ffi.new("char[1]",x[0]) )[0]);
  51. if e == 1 then
  52. return true;
  53. end
  54. return false;
  55. end
  56.  
  57. function doendian(c)
  58. local e = isLittleEndian();
  59. if c == OP_LITTLEENDIAN then
  60. return not e;
  61. elseif c == OP_BIGENDIAN then
  62. return e;
  63. elseif c == OP_NATIVE then
  64. return false;
  65. end
  66. return false;
  67. end
  68.  
  69. function doswap(swap,a,T)
  70. if T == "byte" or T == "char" then
  71. return a;
  72. end
  73. if swap then
  74. -- if T == "double" or T == "float" then
  75. -- this part makes me unhappy --
  76. a = ffi.new(T .. "[1]",a);
  77. local m = ffi.sizeof(T);
  78. local str = ffi.string(a,m):reverse();
  79. ffi.copy(a,str,m);
  80. return tonumber(a[0]);
  81. --else
  82. -- return bit.bswap( a )
  83. --end
  84. end
  85. return a;
  86. end
  87.  
  88. function isdigit(c)
  89. return C.isdigit(string.byte(c)) == 1;
  90. end
  91.  
  92. function l_unpack(s,f,init)
  93. local len = #s;
  94. local i = (init or 1);
  95. local n = 1;
  96. local N = 0;
  97. local cur = OP_NONE;
  98. local swap = false;
  99. --lua_pushnil(L);
  100.  
  101. local values = {}
  102.  
  103. local function push(value)
  104. values[n] = value;
  105. n = n + 1;
  106. end
  107.  
  108. local function done()
  109. return i,unpack(values);
  110. end
  111.  
  112. local endianOp = function(c)
  113. swap = doendian(c);
  114. -- N = 0 -- I don't think this is needed
  115. end
  116.  
  117. local stringOp = function(c)
  118. if i + N - 1 > len then
  119. return done;
  120. end
  121. push(s:sub(i,i + N - 1));
  122. i = i + N;
  123. N = 0;
  124. end
  125. local zstringOp = function(c)
  126. local l = 0;
  127. if i >= len then
  128. return done;
  129. end
  130. local substr = s:sub(i);
  131. l = substr:find('\0');
  132. push(substr:sub(0,l));
  133. i = i + l;
  134. end
  135.  
  136. function unpackNumber(T)
  137. return function()
  138. local m = ffi.sizeof(T) ;
  139. if i + m - 1 > len then
  140. return done;
  141. end
  142. local a = ffi.new(T.."[1]");
  143. ffi.copy(a,s:sub(i,i+m),m);
  144. push(doswap(swap,tonumber(a[0]),T));
  145. i = i + m;
  146. end
  147. end
  148.  
  149. function unpackString(T)
  150. return function()
  151. local m = ffi.sizeof(T);
  152. if i + m > len then
  153. return done;
  154. end
  155. local l = ffi.new(T .. "[1]");
  156. ffi.copy(l,s:sub(i),m);
  157. l = doswap(swap,tonumber(l[0]),T);
  158. if i + m + l - 1 > len then
  159. return done;
  160. end
  161. i = i + m;
  162. push(s:sub(i,i + l - 1));
  163. i = i + l;
  164. end
  165. end
  166.  
  167. local unpack_ops = {
  168. [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,}
  169.  
  170. for c in (f .. '\0'):gmatch('.') do
  171. if not isdigit(c) then
  172. if cur == OP_STRING then
  173. if N == 0 then
  174. push("");
  175. elseif stringOp(cur) == done then
  176. return done();
  177. end
  178. else
  179. if N == 0 then
  180. N = 1;
  181. end
  182. for k = 1,N do
  183. if unpack_ops[cur] then
  184. if unpack_ops[cur](cur) == done then
  185. return done();
  186. end
  187. else
  188. badcode(cur);
  189. end
  190. end
  191. end
  192. cur = c;
  193. N = 0;
  194. else
  195. N = 10 * N + tonumber(c);
  196. end
  197. end
  198. return done();
  199. end
  200.  
  201. function l_pack(f,...)
  202. local args = {f,...};
  203. local i = 1;
  204. local N = 0;
  205. local swap = false;
  206. local b = "";
  207. local cur = OP_NONE;
  208.  
  209. local pop = function()
  210. i = i + 1;
  211. return args[i];
  212. end
  213.  
  214. local endianOp = function(c)
  215. swap = doendian(c);
  216. -- N = 0 -- I don't think this is needed
  217. end
  218.  
  219. local stringOp = function(c)
  220. b = b .. pop();
  221.  
  222. if c == OP_ZSTRING then
  223. b = b .. '\0';
  224. end
  225. end
  226.  
  227. function packNumber(T)
  228. return function()
  229. local a = pop()
  230. a = doswap(swap,T);
  231. a = ffi.new(T .. "[1]",a);
  232. b = b .. ffi.string(a,ffi.sizeof(T));
  233. end
  234. end
  235.  
  236. function packString(T)
  237. return function()
  238. local a = pop();
  239. local l = #a;
  240. local ll = doswap(swap,l,T);
  241. ll = ffi.new(T .. "[1]",ll);
  242. b = b .. ffi.string(ll,ffi.sizeof(T));
  243. b = b .. a;
  244. end
  245. end
  246.  
  247. local pack_ops = {
  248. [OP_LITTLEENDIAN] = endianOp,[OP_ZSTRING] = 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"),}
  249. for c in (f .. '\0'):gmatch('.') do
  250. if not isdigit(c) then
  251. if N == 0 then
  252. N = 1;
  253. end
  254. for k = 1,N do
  255. if pack_ops[cur] then
  256. pack_ops[cur](cur);
  257. else
  258. badcode(cur);
  259. end
  260. end
  261. cur = c;
  262. N = 0;
  263. else
  264. N = 10 * N + tonumber(c);
  265. end
  266. end
  267.  
  268. return b;
  269. end
  270.  
  271. string.pack = l_pack;
  272. string.unpack = l_unpack;



那么借助这个库我们可以这么做:


  1. function Socket.readInt8()
  2. local next,val = string.unpack(tcp:receive(1),"b")
  3. return tonumber(val);
  4. end
  5.  
  6. function Socket.readInt16()
  7. local next,val = string.unpack(tcp:receive(2),"h");
  8. return tonumber(val);
  9. end
  10.  
  11. function Socket.readInt32()
  12. local next,val = string.unpack(tcp:receive(4),"i");
  13. return tonumber(val);
  14. end
  15.  
  16. -- Server string data must end of "\n"
  17. function Socket.readString()
  18. return tostring(tcp:receive());
  19. end
  20.  
  21.  
  22. -- fmt: one or more letter Codes string
  23. -- A : string
  24. -- c : char
  25. -- b : byte (unsigned char)
  26. -- h : short
  27. -- H : unsigned short
  28. -- i : int
  29. -- I : unsigned int
  30. -- l : long
  31. -- L : unsigned long
  32. function Socket.send(fmt,...)
  33. tcp:send(string.pack(fmt,...));
  34. end

读取数据我们使用lua socket的receive方法截取数据,以后再用解包函数解包,以后再强转成我们需要的类型。读取数据直接把数据按照类型打包,send出去即可。





第二部分,node.js的数据读写


node.js 我只是使用了原生的socket API 并没有使用任何框架。封装了一个数据读写的模块如下:


  1. var BufferRead = function(buff) {
  2. var offset = 0;
  3.  
  4. return {
  5. readInt8: function() {
  6. var int8 = buff.readInt8(offset);
  7. offset += 1;
  8. return int8;
  9. },readInt16: function() {
  10. var int16 = buff.readInt16LE(offset);
  11. offset += 2;
  12. return int16;
  13. },readInt32: function() {
  14. var int32 = buff.readInt32LE(offset);
  15. offset += 4;
  16. return int32;
  17. },readString: function(len) {
  18. var str = buff.toString("utf8",offset,offset + len);
  19. offset += len;
  20. return str;
  21. }
  22. };
  23. }
  24.  
  25.  
  26. var BufferWrite = function(socket) {
  27. return {
  28. writeInt8: function(int8) {
  29. var buff = new Buffer(1);
  30. buff.writeInt8(int8,0);
  31. socket.write(buff);
  32. },writeInt16: function(int16) {
  33. var buff = new Buffer(2);
  34. buff.writeInt16LE(int16,writeInt32: function(int32) {
  35. var buff = new Buffer(4);
  36. buff.writeInt32LE(int32,writeString: function(str) {
  37. socket.write(str);
  38. },/**
  39. * fmt is format string
  40. * A : string
  41. * b : byte (unsigned char)
  42. * h : short
  43. * i : int
  44. */
  45. write: function(fmt) {
  46. for (var i = 0; i < fmt.length; i++) {
  47. switch (fmt.charAt(i)) {
  48. case 'A':
  49. this.writeString(arguments[i + 1]);
  50. break;
  51. case 'b':
  52. this.writeInt8(arguments[i + 1]);
  53. break;
  54. case 'h':
  55. this.writeInt16(arguments[i + 1]);
  56. break;
  57. case 'i':
  58. this.writeInt32(arguments[i + 1]);
  59. break;
  60. }
  61. }
  62. }
  63. };
  64. }
  65.  
  66.  
  67.  
  68. module.exports = {
  69. BufferRead: BufferRead,BufferWrite: BufferWrite
  70. };


读写数据只是利用node.js提供的Buff对象打包了数据以后用socket进行操作清晰明了。


  1. var protocal = require("./Protocol.js");
  2. var net = require("net");
  3. var buffData = require("./BufferData.js");
  4.  
  5. var server = net.createServer();
  6.  
  7.  
  8. server.listen(1024,function() {
  9. console.log('Server start local host at port 1024');
  10. });
  11.  
  12. server.on("connection",function(socket) {
  13. console.log("server socket connected");
  14. socket.on('data',function(buff) {
  15. var buffRead = new buffData.BufferRead(buff);
  16. var buffWrite = new buffData.BufferWrite(socket);
  17. var reqCode = buffRead.readInt32();
  18. protocal.handlers[reqCode](socket,buffRead,buffWrite);
  19. socket.pipe(socket);
  20. });
  21. socket.on('end',function() {
  22. console.log('server socket disconnected');
  23. socket.destroy();
  24. });
  25. socket.on('error',function(error) {
  26. console.log("Client error: %s",error.toString());
  27. socket.destroy();
  28. });
  29. });
  30.  
  31. server.on("error",function (error) {
  32. console.log("Server error code = %s",error.toString());
  33. });
  34.  
  35. server.on("close",function() {
  36. console.log("Server closed");
  37. });
这是服务器启动的代码,关键在入on data的回调函数,我们利用系统提供的buff和socket对象,构建我们封装的BuffRead和BuffWrite就可以进行数据的读写了。

猜你在找的Cocos2d-x相关文章