skynet-网络层处理,及使用sproto

  • 一开始打算用luasocket这个开源的项目,但是接入进cocos中后,在读到流的长度和服务端下发的一直,但是在解析的时候解不出来,push不进lua的stack中。断点跟过一下代码,还是找不出原因(懒癌发作不想看里面的代码),于是乎就写了个简单网络处理(没有做跨平台处理)。
  • 因为用是云风的 sproto 作为rpc框架,里面支持 lua53 ,对流的处理也有api string.packstring.unpack,使得断包粘包处理非常简单。

用的是c++11的线程库和锁,使得代码变得相对简单
大体步骤:

  1. 开两个线程,send thread 和 recv thread。(可以开多一个线程 connect thread,这样连接的时候就可以转转菊花了)。
  2. 开两个char buffer缓冲池,send buffer 和 recv buffer,用来保存 主线程send 的消息,和 recv thread 接收到的服务端的消息。同时可以开两个 mutex 变量,分别用来锁这两个buffer
  3. 主线程开个定时器,定时去扫 recv buffer,看有没有服务端来的消息,如果有,就用 mutex lock 一下,把里面的消息读出来处理。
  4. send thread 直接就一个 while true 死循环,如果 send buffer 中有需要发送给服务端的消息,就用 mutex lock 一下,把消息读出来 send 到服务端

示意图:
这里写图片描述

这些相关代码我都丢到一个vs工程中,只需要关联进来或者打成 lib or dll 导入。
代码已经放在 github
相关代码我也贴一下


提供几个接口给lua调用

lnetReg.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <lua.h>
#include <lauxlib.h>
#include <stdbool.h>

#define LUA_API_EXPORT __declspec(dllexport)

extern bool Connect(const char* _ip, int _port);
extern void Send(const char* _msg, int _len);
extern void Recv(char* _msg, unsigned int* _len);
extern void Close();

static int
lconnect(lua_State *L) {
size_t sz = 0;
const char* ip = luaL_checklstring(L, 1, &sz);
int port = luaL_checkinteger(L, 2);
bool ret = Connect(ip, port);
lua_pushboolean(L, ret);
return 1;
}

static int
lsend(lua_State *L) {
size_t sz = 0;
const char* msg = luaL_checklstring(L, 1, &sz);
Send(msg, sz);
return 1;
}

static int
lrecv(lua_State *L) {
unsigned int len = 0;
char buffer[65535];
Recv(buffer, &len);
if (len > 0)
{
lua_pushlstring(L, buffer, len);
return 1;
}
else
{
lua_pushnil(L);
lua_pushinteger(L, 1); //lua中根据第二个来判断是否返回
return 2;
}
}

static int
lclose(lua_State *L) {
Close();
return 1;
}

LUA_API_EXPORT int
luaopen_network(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "connect", lconnect },
{ "send", lsend },
{ "recv", lrecv },
{ "close", lclose },
{ NULL, NULL },
};
//luaL_newlib(L, l);
lua_newtable(L);
luaL_openlib(L, "network", l, 0);
return 1;
}

lua中的断包粘包处理在这个工程中,在 rpcMgr.lua 里面,传送门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
local function handle_request (name, args, response)
print ("--- 【S>>C】, request from server:", name)

-- if args then
-- dump (args)
-- end

local f = REQUEST[name]
if f then
local ret = f(nil, args)
if ret and response then
send_message (response (ret))
end
else
print("--- handle_request, not found func:"..s.name)
end
end

local function handle_response (id, args)
local s = assert (session[id])
session[id] = nil
local f = RESPONSE[s.name]

print ("--- 【S>>C】, response from server:", s.name)
-- dump (args)

if f then
f (s.args, args)
else
print("--- handle_response, not found func:"..s.name)
end
end

local function handle_message (t, ...) -- 解析出消息后的对应处理
if t == "REQUEST" then
handle_request (...)
else
handle_response (...)
end
end

local function unpack (text) -- 解包函数,断包粘包处理
if not text then return end

local size = #text
if size < 2 then
return nil, text
end
local s = text:byte (1) * 256 + text:byte (2)

print(string.format("--- unpacking, realSize:%d, expectSize:%d",size, s))
if size < s + 2 then
return nil, text
end

return text:sub (3, 2 + s), text:sub (3 + s)
end

local last = ""
local function recv (last)
local result
result, last = unpack (last)
if result then
return result, last
end

local r, err = network:recv() -- 读取recv buff中的数据
if r then
print("--- socket recv, r, len:", #r, r)
end
if err then
return nil, last
end
if r == "" then
error (string.format ("socket closed"))
end

return unpack (last .. r)
end

function RpcMgr.schedulerReceive( ... ) -- 主线程中的定时器定时执行
local function dispatch_message ()
while true do
local v
v, last = recv(last)
if not v then
break
end

handle_message (host:dispatch (v))
end
end

dispatch_message()
end