1. 程式人生 > >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 如下:

    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 <[email protected]>
-- 29 Jun 2007 19:27:20
-- This code is hereby placed in the public domain.
-- with contributions from Ignacio Castaño <[email protected]> and
-- Roberto Ierusalimschy <
[email protected]
>. -- 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[n] = 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 3.x Lua socket node.js 利用scoket互相通訊二進位制資料

第一部分,Lua socket如何讀寫二進位制資料。 cocos2dx 3.x 版本已經集成了lua socket所以可以直接使用無需自己整合。首先需要初始化lua socket 如下: socket = require("socket"); tcp

Python 3.x的編碼解碼問題

最近在極客學院學習爬蟲,老師用的是2.x版本,而我電腦裡的版本是3.x,於是在網頁上查詢在輸出中文時如何正確輸出。 我原以為2.x 3.x在很多語法上沒什麼區別,在解碼decode上應該也差不多,沒想到竟然發現,很多部落格提到2.x提出的解決方案 py

Urllib庫在python2.x3.x的區別聯絡

urllib庫是python提供的一個用於操作URL的模組,在python2中有urllib和urllib2,在python3中urllib2合併到urllib中,區別和聯絡如下: 1) 在python2中使用的import urllib2——對應的,在python3中使用

cocos2dx(3.X)使用shader

一 shader的基本概念 1 什麼是shader shader即著色器,就是專門用來渲染3D圖形的一種技術。 通過shader,可以自己編寫顯示卡渲染畫面的演算法,使畫面更漂亮、更逼真。 2 shader分類 shader又分兩種,一種是

將舊專案基於cocos2dx 2.x的除錯繪製轉移到cocos2dx 3.x

1、首先必須修改原先在draw函式中繪製渲染的方式。3.x不是直接呼叫draw函式進行繪製的,而是通過renderCommand進行延時渲染。 老專案的渲染方式-draw函式中呼叫 #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS

node.js的fs核心模組檔案操作 -----由淺入深

https://www.cnblogs.com/null11/p/7499091.html node.js的fs核心模組讀寫檔案操作 -----由淺入深 node.js 裡fs模組 常用的功能 實現檔案的讀寫 目錄的操作 - 同步和非同步共存 ,有非同步不用同步 - fs.

cocos2dx 3.2 SequenceSpawn無法執行RepeatForever動作的問題解決

   (博主qq,1204802552,歡迎交流)  有時候,我們想在Sequence或者Spawn中加入RepeatForever的動作,但在實際執行中,該迴圈動作並未得到執行,我們先來說下解決的方法。    對於Spawn,很簡單,我們只需要把RepeatForever

cocos2dx 3.x 物理引擎接觸檢測掩碼、碰撞掩碼類別掩碼問題解析

言簡意賅,物體的類別掩碼CategoryBitMask和物體的接觸測試掩碼ContactTestBitmask邏輯“與”運算結果非零,則觸發EventListenerPhysicsContact事件監聽器,物體的類別掩碼CategoryBitMask和物體

android gradle tools 3.X 依賴,implementation compile區別

前言 2017 年google 後,Android studio 版本更新至3.0,更新中,連帶著com.android.tools.build:gradle 工具也升級到了3.0.0,在3.0.0中使用了最新的Gralde 4.0 里程碑版本作為gradle 的編譯版

mybatis 3.5.0/mybatis plus 3.xdao層與xml引數繫結大全(老版本的不一樣)

方式1(不推薦) 單個引數方式 @Mapper public interface UserDao extends BaseMa

Cocos2d-X的聲音音效

循環 volume tid ng- cocos2d pop sin 返回值 source 在玩遊戲時都會有各種遊戲音,如啟動遊戲時會有背景音,勝利或者失敗會有一些音效。在Cocos2d-X中能夠使用CocosDenshion實現各種聲音 在使用CocosDensh

【轉】 cocos2dx 3.x C++搭建protobuf環境

person ccf binding csdn bind taf protoc -cp strlen http://blog.csdn.net/ganpengjin1/article/details/50964961 Cocos2dx 裏面在網絡遊戲通信這一塊一般我們都會采

Python 3.x使用urllib出現AttributeError: module 'urllib' has no attribute 'request'錯誤

剛剛開始學習爬蟲,開始寫部落格打算把錯誤記錄下來,已杜自己忘記,並給同樣的小白幫助 python 3.x中urllib庫和urilib2庫合併成了urllib庫,python3.X中應該使用urllib.request,即替換掉(python中的)urllib2成urllib.request

Django2.1.3框架(fields.E300)(fields.E307)報錯處理

        使用Django框架建立了Web專案,修改了模型models.py之後,執行資料庫遷移操作,出現如下報錯: models.py內容如下: from django.db import models from django.contrib.aut

Python 3.6reduce函式cmp函式

1,Python 3中取消了cmp函式,使用cmp功能的話需按如下所示(方法一種): import operator #help(operator) ... __ge__ = ge(...) ge(a, b) -- Same as a>=b. __eq__ = eq(...

android隱藏虛擬導航欄-------cocos2dx 3.x

查到網上的說法 android 沉浸式狀態列, 百度百科。沉浸式就是要給使用者提供完全沉浸的體驗,使使用者有一種置身於虛擬世界之中的感覺。比如說現在大熱的VR就是主打的沉浸式體驗。 其實想實現的效果就是——————————–隱藏狀態列 一個Android應用程式的介面上其實是有很多

cocos2dx[3.x](3)——大鉅變3.x

cocos2d-x v3.0 釋出說明 原文地址https://github.com/fusijie/Cocos2dx-Release-Note/blob/master/cocos2d-x_v3.0_release_notes.md#%E7%8E%AF%E5%A

Python 3.x的6種標準物件型別之——Number(數值)資料型別

整型(int) 通常被稱為整型或整數,包含正負,不含小數點,同時沒有大小限制。 (如1,2,3,-6,5555等) 支援正常的數學運算,但在整數的除法(/)中,計算結果不會為整數 print(153/51) >>> 3.0 若想在整數的除法中

筆記3.linux的使用者許可權管理

  使用者的管理 1,檢視當前使用者:whoami。 2,通過cat /etc/passwd 檢視系統使用者資訊。 3,檢視所有登入系統的使用者資訊 :who          常用選項:

cocos2dx 3.x版本多邊形剛體

//多邊形的點: Point verts1[] = { Point(-146.5f, 155.1f), Point(-146.5f, -87.6f),