mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-22 02:53:09 +00:00
Compare commits
124 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2b253b40 | ||
|
|
f65ecdf767 | ||
|
|
ee3f9c6ecd | ||
|
|
52134f9e57 | ||
|
|
21cd5c9882 | ||
|
|
23587d0b0f | ||
|
|
ce6fa906d3 | ||
|
|
0e20fe5b4e | ||
|
|
978d6af359 | ||
|
|
812088b5ab | ||
|
|
bf54a66346 | ||
|
|
5fb4580108 | ||
|
|
f1c9f9b9dc | ||
|
|
f15cd1d458 | ||
|
|
24a2df9383 | ||
|
|
176e4df90c | ||
|
|
babd730d07 | ||
|
|
fe9640e2dd | ||
|
|
906008f667 | ||
|
|
2da2f121c8 | ||
|
|
a0d2c7172c | ||
|
|
463a789898 | ||
|
|
175529b114 | ||
|
|
6f6039c136 | ||
|
|
2b13eb250d | ||
|
|
84cb4bb4c1 | ||
|
|
784fb9fee7 | ||
|
|
ff6c92b975 | ||
|
|
64801972e7 | ||
|
|
ee61e60631 | ||
|
|
c8c95f1690 | ||
|
|
50344af335 | ||
|
|
3be0f50722 | ||
|
|
6ee014dd99 | ||
|
|
2a4bee5bdd | ||
|
|
4f21696387 | ||
|
|
ae041c97b3 | ||
|
|
6df72e5842 | ||
|
|
b9e06dc065 | ||
|
|
b3cf1bc625 | ||
|
|
20b181c56d | ||
|
|
8e4a175155 | ||
|
|
cfe9506a5a | ||
|
|
fa78623b1c | ||
|
|
e06a9e3701 | ||
|
|
c87dea3d9b | ||
|
|
929ce385a3 | ||
|
|
765749f608 | ||
|
|
5fad5d63ae | ||
|
|
af10c59eaf | ||
|
|
a77b71530d | ||
|
|
803a79059a | ||
|
|
6c9ad16077 | ||
|
|
2ab689f7c1 | ||
|
|
f8a50929a8 | ||
|
|
af3ca3bb2f | ||
|
|
aaf8617dd5 | ||
|
|
1d3e393fd4 | ||
|
|
c3df405e33 | ||
|
|
b1ef8a33d9 | ||
|
|
954625a530 | ||
|
|
9850ca1b94 | ||
|
|
01e1a6aafa | ||
|
|
0f4a771792 | ||
|
|
73bd788a6c | ||
|
|
8cb5378df9 | ||
|
|
82bddd9cb8 | ||
|
|
24ff08f997 | ||
|
|
fc8983227d | ||
|
|
9bdc36526e | ||
|
|
24857a3d8d | ||
|
|
b221c9e84c | ||
|
|
8d2fcef273 | ||
|
|
1d8ca31d55 | ||
|
|
3b95ecd9b2 | ||
|
|
7cc25a6238 | ||
|
|
1ef5430e0a | ||
|
|
b39fc6ee23 | ||
|
|
a074cd79bd | ||
|
|
883757b936 | ||
|
|
35e5a938b0 | ||
|
|
31d7b648b4 | ||
|
|
44e6693a40 | ||
|
|
c458eba6b4 | ||
|
|
6d51c21c06 | ||
|
|
d2aa2069f7 | ||
|
|
8de9c22fd7 | ||
|
|
d649d0adf1 | ||
|
|
0aa5274799 | ||
|
|
bfc3fd425a | ||
|
|
05f508fb42 | ||
|
|
c8b8dc1276 | ||
|
|
1c62785d71 | ||
|
|
4a8bf4f39a | ||
|
|
2f6bfe9104 | ||
|
|
535bbc7219 | ||
|
|
9713b055a0 | ||
|
|
7dee9a97c5 | ||
|
|
aab65a2728 | ||
|
|
c8775ee6f5 | ||
|
|
016b4dc58f | ||
|
|
36d77aaa2b | ||
|
|
f32f613ac0 | ||
|
|
885c5b56bb | ||
|
|
b923f93aee | ||
|
|
ce6efc535c | ||
|
|
320a608c36 | ||
|
|
f47e4275b6 | ||
|
|
228ad16d74 | ||
|
|
9b901c6a38 | ||
|
|
37d64a7028 | ||
|
|
f8bf05513c | ||
|
|
bac9348db9 | ||
|
|
3074dbea0b | ||
|
|
fe4122e579 | ||
|
|
e9d4075e43 | ||
|
|
fce05f0cfc | ||
|
|
4cce476dfc | ||
|
|
ad5c37200b | ||
|
|
c0e1365dc2 | ||
|
|
984f727385 | ||
|
|
315945b2bd | ||
|
|
bf686da723 | ||
|
|
6733134ad4 |
@@ -403,7 +403,7 @@ static int traverseephemeron (global_State *g, Table *h) {
|
||||
reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
|
||||
}
|
||||
}
|
||||
if (prop)
|
||||
if (g->gcstate != GCSatomic || prop)
|
||||
linktable(h, &g->ephemeron); /* have to propagate again */
|
||||
else if (hasclears) /* does table have white keys? */
|
||||
linktable(h, &g->allweak); /* may have to clean white keys */
|
||||
|
||||
60
HISTORY.md
60
HISTORY.md
@@ -1,3 +1,63 @@
|
||||
v0.9.3 (2014-1-5)
|
||||
-----------
|
||||
* Add : mongo createIndex
|
||||
* Update : sproto
|
||||
* bugfix : sharedata check dirty flag when len/pairs metamethod
|
||||
* bugfix : multicast
|
||||
|
||||
v0.9.2 (2014-12-8)
|
||||
-----------
|
||||
* Simplify the message queue
|
||||
* Add create_index in mongo driver
|
||||
* Fix a bug in big-endian architecture (sproto)
|
||||
|
||||
v0.9.0 / v0.9.1 (2014-11-17)
|
||||
-----------
|
||||
* Add UDP support
|
||||
* Add IPv6 support
|
||||
* socket send package can define a release method
|
||||
* dispatch read before write in epoll
|
||||
* remove snax queue mode
|
||||
* Fix a bug in big-endian architecture
|
||||
|
||||
v0.8.1 (2014-11-3)
|
||||
-----------
|
||||
* Send to an invalid remote service will raise an error
|
||||
* Bugifx: socket open address string
|
||||
* Remove sha1 from mysqlaux
|
||||
* merge lua and sproto bugfix , use crypt lib instead
|
||||
* Fix a memory leak in socket
|
||||
* minor bugfix in http module
|
||||
|
||||
v0.8.0 (2014-10-27)
|
||||
-----------
|
||||
* Add mysql client driver
|
||||
* Bugfix : skynet.queue
|
||||
|
||||
v0.7.4 (2014-10-13)
|
||||
-----------
|
||||
* Bugfix : clear coroutine pool when GC
|
||||
* hotfix : A bug introduce by 0.7.3
|
||||
|
||||
v0.7.3 (2014-10-13)
|
||||
-----------
|
||||
* Add some logs (warning) when overload
|
||||
* Bugfix: crash on exit
|
||||
|
||||
v0.7.2 (2014-9-29)
|
||||
-----------
|
||||
* Bugfix : datacenter.wait
|
||||
* Bugfix : error in forker coroutine
|
||||
* Add skynet.term
|
||||
* Accept socket report port
|
||||
* sharedata can be update more than once
|
||||
|
||||
v0.7.1 (2014-9-22)
|
||||
-----------
|
||||
* bugfix: wakeup sleep should return BREAK
|
||||
* bugfix: sharedatad load string
|
||||
* bugfix: dataserver forward error msg
|
||||
|
||||
v0.7.0 (2014-9-8)
|
||||
-----------
|
||||
* Use sproto instead of cjson
|
||||
|
||||
6
Makefile
6
Makefile
@@ -43,7 +43,8 @@ jemalloc : $(MALLOC_STATICLIB)
|
||||
CSERVICE = snlua logger gate harbor
|
||||
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack \
|
||||
clientsocket memory profile multicast \
|
||||
cluster crypt sharedata stm sproto lpeg
|
||||
cluster crypt sharedata stm sproto lpeg \
|
||||
mysqlaux
|
||||
|
||||
SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \
|
||||
skynet_server.c skynet_start.c skynet_timer.c skynet_error.c \
|
||||
@@ -122,6 +123,9 @@ $(LUA_CLIB_PATH)/sproto.so : lualib-src/sproto/sproto.c lualib-src/sproto/lsprot
|
||||
$(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) -I3rd/lpeg $^ -o $@
|
||||
|
||||
$(LUA_CLIB_PATH)/mysqlaux.so : lualib-src/lua_mysqlaux.c | $(LUA_CLIB_PATH)
|
||||
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
|
||||
|
||||
clean :
|
||||
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so
|
||||
|
||||
|
||||
11
examples/config.mysql
Normal file
11
examples/config.mysql
Normal file
@@ -0,0 +1,11 @@
|
||||
root = "./"
|
||||
thread = 8
|
||||
logger = nil
|
||||
harbor = 0
|
||||
start = "main_mysql" -- main script
|
||||
bootstrap = "snlua bootstrap" -- The service for bootstrap
|
||||
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
|
||||
lualoader = "lualib/loader.lua"
|
||||
snax = root.."examples/?.lua;"..root.."test/?.lua"
|
||||
cpath = root.."cservice/?.so"
|
||||
-- daemon = "./skynet.pid"
|
||||
@@ -1,9 +1,16 @@
|
||||
local skynet = require "skynet"
|
||||
local harbor = require "skynet.harbor"
|
||||
|
||||
local function monitor_master()
|
||||
harbor.linkmaster()
|
||||
print("master is down")
|
||||
skynet.exit()
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
print("Log server start")
|
||||
skynet.monitor "simplemonitor"
|
||||
local log = skynet.newservice("globallog")
|
||||
skynet.exit()
|
||||
skynet.fork(monitor_master)
|
||||
end)
|
||||
|
||||
|
||||
10
examples/main_mysql.lua
Normal file
10
examples/main_mysql.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
local skynet = require "skynet"
|
||||
|
||||
|
||||
skynet.start(function()
|
||||
print("Main Server start")
|
||||
local console = skynet.newservice("testmysql")
|
||||
|
||||
print("Main Server exit")
|
||||
skynet.exit()
|
||||
end)
|
||||
@@ -66,6 +66,7 @@ skynet.start(function()
|
||||
end
|
||||
local balance = 1
|
||||
local id = socket.listen("0.0.0.0", 8001)
|
||||
skynet.error("Listen web port 8001")
|
||||
socket.start(id , function(id, addr)
|
||||
skynet.error(string.format("%s connected, pass it to agent :%08x", addr, agent[balance]))
|
||||
skynet.send(agent[balance], "lua", id)
|
||||
|
||||
@@ -10,6 +10,7 @@ local gate
|
||||
local agent = {}
|
||||
|
||||
function SOCKET.open(fd, addr)
|
||||
skynet.error("New client from : " .. addr)
|
||||
agent[fd] = skynet.newservice("agent")
|
||||
skynet.call(agent[fd], "lua", "start", gate, fd, proto)
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ mc_unpacklocal(lua_State *L) {
|
||||
if (sz != sizeof(*pack)) {
|
||||
return luaL_error(L, "Invalid multicast package size %d", sz);
|
||||
}
|
||||
lua_settop(L, 1);
|
||||
lua_pushlightuserdata(L, *pack);
|
||||
lua_pushlightuserdata(L, (*pack)->data);
|
||||
lua_pushunsigned(L, (*pack)->size);
|
||||
return 3;
|
||||
@@ -92,6 +92,8 @@ mc_unpacklocal(lua_State *L) {
|
||||
/*
|
||||
lightuserdata struct mc_package **
|
||||
integer reference
|
||||
|
||||
return mc_package *
|
||||
*/
|
||||
static int
|
||||
mc_bindrefer(lua_State *L) {
|
||||
@@ -102,16 +104,17 @@ mc_bindrefer(lua_State *L) {
|
||||
}
|
||||
(*pack)->reference = ref;
|
||||
|
||||
return 0;
|
||||
lua_pushlightuserdata(L, *pack);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
lightuserdata struct mc_package **
|
||||
lightuserdata struct mc_package *
|
||||
*/
|
||||
static int
|
||||
mc_closelocal(lua_State *L) {
|
||||
struct mc_package **ptr = lua_touserdata(L,1);
|
||||
struct mc_package *pack = *ptr;
|
||||
struct mc_package *pack = lua_touserdata(L,1);
|
||||
|
||||
int ref = __sync_sub_and_fetch(&pack->reference, 1);
|
||||
if (ref <= 0) {
|
||||
|
||||
@@ -302,9 +302,9 @@ filter_data(lua_State *L, int fd, uint8_t * buffer, int size) {
|
||||
}
|
||||
|
||||
static void
|
||||
pushstring(lua_State *L, const char * msg) {
|
||||
pushstring(lua_State *L, const char * msg, int size) {
|
||||
if (msg) {
|
||||
lua_pushstring(L, msg);
|
||||
lua_pushlstring(L, msg, size);
|
||||
} else {
|
||||
lua_pushliteral(L, "");
|
||||
}
|
||||
@@ -350,12 +350,12 @@ lfilter(lua_State *L) {
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_OPEN));
|
||||
// ignore listen id (message->id);
|
||||
lua_pushinteger(L, message->ud);
|
||||
pushstring(L, buffer);
|
||||
pushstring(L, buffer, size);
|
||||
return 4;
|
||||
case SKYNET_SOCKET_TYPE_ERROR:
|
||||
lua_pushvalue(L, lua_upvalueindex(TYPE_ERROR));
|
||||
lua_pushinteger(L, message->id);
|
||||
pushstring(L, buffer);
|
||||
pushstring(L, buffer, size);
|
||||
return 4;
|
||||
default:
|
||||
// never get here
|
||||
|
||||
@@ -120,37 +120,37 @@ rb_read(struct read_block *rb, void *buffer, int sz) {
|
||||
|
||||
static inline void
|
||||
wb_nil(struct write_block *wb) {
|
||||
int n = TYPE_NIL;
|
||||
uint8_t n = TYPE_NIL;
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_boolean(struct write_block *wb, int boolean) {
|
||||
int n = COMBINE_TYPE(TYPE_BOOLEAN , boolean ? 1 : 0);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_BOOLEAN , boolean ? 1 : 0);
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_integer(struct write_block *wb, int v, int type) {
|
||||
if (v == 0) {
|
||||
int n = COMBINE_TYPE(type , 0);
|
||||
uint8_t n = COMBINE_TYPE(type , 0);
|
||||
wb_push(wb, &n, 1);
|
||||
} else if (v<0) {
|
||||
int n = COMBINE_TYPE(type , 4);
|
||||
uint8_t n = COMBINE_TYPE(type , 4);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 4);
|
||||
} else if (v<0x100) {
|
||||
int n = COMBINE_TYPE(type , 1);
|
||||
uint8_t n = COMBINE_TYPE(type , 1);
|
||||
wb_push(wb, &n, 1);
|
||||
uint8_t byte = (uint8_t)v;
|
||||
wb_push(wb, &byte, 1);
|
||||
} else if (v<0x10000) {
|
||||
int n = COMBINE_TYPE(type , 2);
|
||||
uint8_t n = COMBINE_TYPE(type , 2);
|
||||
wb_push(wb, &n, 1);
|
||||
uint16_t word = (uint16_t)v;
|
||||
wb_push(wb, &word, 2);
|
||||
} else {
|
||||
int n = COMBINE_TYPE(type , 4);
|
||||
uint8_t n = COMBINE_TYPE(type , 4);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 4);
|
||||
}
|
||||
@@ -158,14 +158,14 @@ wb_integer(struct write_block *wb, int v, int type) {
|
||||
|
||||
static inline void
|
||||
wb_number(struct write_block *wb, double v) {
|
||||
int n = COMBINE_TYPE(TYPE_NUMBER , 8);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_NUMBER , 8);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, 8);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wb_pointer(struct write_block *wb, void *v) {
|
||||
int n = TYPE_USERDATA;
|
||||
uint8_t n = TYPE_USERDATA;
|
||||
wb_push(wb, &n, 1);
|
||||
wb_push(wb, &v, sizeof(v));
|
||||
}
|
||||
@@ -173,13 +173,13 @@ wb_pointer(struct write_block *wb, void *v) {
|
||||
static inline void
|
||||
wb_string(struct write_block *wb, const char *str, int len) {
|
||||
if (len < MAX_COOKIE) {
|
||||
int n = COMBINE_TYPE(TYPE_SHORT_STRING, len);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_SHORT_STRING, len);
|
||||
wb_push(wb, &n, 1);
|
||||
if (len > 0) {
|
||||
wb_push(wb, str, len);
|
||||
}
|
||||
} else {
|
||||
int n;
|
||||
uint8_t n;
|
||||
if (len < 0x10000) {
|
||||
n = COMBINE_TYPE(TYPE_LONG_STRING, 2);
|
||||
wb_push(wb, &n, 1);
|
||||
@@ -201,11 +201,11 @@ static int
|
||||
wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) {
|
||||
int array_size = lua_rawlen(L,index);
|
||||
if (array_size >= MAX_COOKIE-1) {
|
||||
int n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1);
|
||||
wb_push(wb, &n, 1);
|
||||
wb_integer(wb, array_size,TYPE_NUMBER);
|
||||
} else {
|
||||
int n = COMBINE_TYPE(TYPE_TABLE, array_size);
|
||||
uint8_t n = COMBINE_TYPE(TYPE_TABLE, array_size);
|
||||
wb_push(wb, &n, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -747,9 +747,6 @@ lupdate(lua_State *L) {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
struct ctrl * c= lua_touserdata(L, 1);
|
||||
struct table *n = lua_touserdata(L, 2);
|
||||
if (c->update) {
|
||||
return luaL_error(L, "can't update more than once");
|
||||
}
|
||||
if (c->root == n) {
|
||||
return luaL_error(L, "You should update a new object");
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "skynet_socket.h"
|
||||
|
||||
#define BACKLOG 32
|
||||
@@ -80,6 +83,15 @@ lnewbuffer(lua_State *L) {
|
||||
int size
|
||||
|
||||
return size
|
||||
|
||||
Comment: The table pool record all the buffers chunk,
|
||||
and the first index [1] is a lightuserdata : free_node. We can always use this pointer for struct buffer_node .
|
||||
The following ([2] ...) userdatas in table pool is the buffer chunk (for struct buffer_node),
|
||||
we never free them until the VM closed. The size of first chunk ([2]) is 8 struct buffer_node,
|
||||
and the second size is 16 ... The largest size of chunk is LARGE_PAGE_NODE (4096)
|
||||
|
||||
lpushbbuffer will get a free struct buffer_node from table pool, and then put the msg/size in it.
|
||||
lpopbuffer return the struct buffer_node back to table pool (By calling return_free_node).
|
||||
*/
|
||||
static int
|
||||
lpushbuffer(lua_State *L) {
|
||||
@@ -372,28 +384,64 @@ lunpack(lua_State *L) {
|
||||
} else {
|
||||
lua_pushlightuserdata(L, message->buffer);
|
||||
}
|
||||
if (message->type == SKYNET_SOCKET_TYPE_UDP) {
|
||||
int addrsz = 0;
|
||||
const char * addrstring = skynet_socket_udp_address(message, &addrsz);
|
||||
if (addrstring) {
|
||||
lua_pushlstring(L, addrstring, addrsz);
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
static const char *
|
||||
address_port(lua_State *L, char *tmp, const char * addr, int port_index, int *port) {
|
||||
const char * host;
|
||||
if (lua_isnoneornil(L,port_index)) {
|
||||
host = strchr(addr, '[');
|
||||
if (host) {
|
||||
// is ipv6
|
||||
++host;
|
||||
const char * sep = strchr(addr,']');
|
||||
if (sep == NULL) {
|
||||
luaL_error(L, "Invalid address %s.",addr);
|
||||
}
|
||||
memcpy(tmp, host, sep-host);
|
||||
tmp[sep-host] = '\0';
|
||||
host = tmp;
|
||||
sep = strchr(sep + 1, ':');
|
||||
if (sep == NULL) {
|
||||
luaL_error(L, "Invalid address %s.",addr);
|
||||
}
|
||||
*port = strtoul(sep+1,NULL,10);
|
||||
} else {
|
||||
// is ipv4
|
||||
const char * sep = strchr(addr,':');
|
||||
if (sep == NULL) {
|
||||
luaL_error(L, "Invalid address %s.",addr);
|
||||
}
|
||||
memcpy(tmp, addr, sep-addr);
|
||||
tmp[sep-addr] = '\0';
|
||||
host = tmp;
|
||||
*port = strtoul(sep+1,NULL,10);
|
||||
}
|
||||
} else {
|
||||
host = addr;
|
||||
*port = luaL_optinteger(L,port_index, 0);
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
static int
|
||||
lconnect(lua_State *L) {
|
||||
size_t sz = 0;
|
||||
const char * addr = luaL_checklstring(L,1,&sz);
|
||||
char tmp[sz];
|
||||
int port;
|
||||
const char * host;
|
||||
if (lua_isnoneornil(L,2)) {
|
||||
const char * sep = strchr(addr,':');
|
||||
if (sep == NULL) {
|
||||
return luaL_error(L, "Connect to invalid address %s.",addr);
|
||||
}
|
||||
memcpy(tmp, addr, sep-addr);
|
||||
tmp[sep-addr] = '\0';
|
||||
host = tmp;
|
||||
port = strtoul(sep+1,NULL,10);
|
||||
} else {
|
||||
host = addr;
|
||||
port = luaL_checkinteger(L,2);
|
||||
int port = 0;
|
||||
const char * host = address_port(L, tmp, addr, 2, &port);
|
||||
if (port == 0) {
|
||||
return luaL_error(L, "Invalid port");
|
||||
}
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int id = skynet_socket_connect(ctx, host, port);
|
||||
@@ -426,14 +474,14 @@ llisten(lua_State *L) {
|
||||
}
|
||||
|
||||
static void *
|
||||
get_buffer(lua_State *L, int *sz) {
|
||||
get_buffer(lua_State *L, int index, int *sz) {
|
||||
void *buffer;
|
||||
if (lua_isuserdata(L,2)) {
|
||||
buffer = lua_touserdata(L,2);
|
||||
*sz = luaL_checkinteger(L,3);
|
||||
if (lua_isuserdata(L,index)) {
|
||||
buffer = lua_touserdata(L,index);
|
||||
*sz = luaL_checkinteger(L,index+1);
|
||||
} else {
|
||||
size_t len = 0;
|
||||
const char * str = luaL_checklstring(L, 2, &len);
|
||||
const char * str = luaL_checklstring(L, index, &len);
|
||||
buffer = skynet_malloc(len);
|
||||
memcpy(buffer, str, len);
|
||||
*sz = (int)len;
|
||||
@@ -446,7 +494,7 @@ lsend(lua_State *L) {
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int id = luaL_checkinteger(L, 1);
|
||||
int sz = 0;
|
||||
void *buffer = get_buffer(L, &sz);
|
||||
void *buffer = get_buffer(L, 2, &sz);
|
||||
int err = skynet_socket_send(ctx, id, buffer, sz);
|
||||
lua_pushboolean(L, !err);
|
||||
return 1;
|
||||
@@ -457,7 +505,7 @@ lsendlow(lua_State *L) {
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int id = luaL_checkinteger(L, 1);
|
||||
int sz = 0;
|
||||
void *buffer = get_buffer(L, &sz);
|
||||
void *buffer = get_buffer(L, 2, &sz);
|
||||
skynet_socket_send_lowpriority(ctx, id, buffer, sz);
|
||||
return 0;
|
||||
}
|
||||
@@ -486,6 +534,90 @@ lnodelay(lua_State *L) {
|
||||
skynet_socket_nodelay(ctx,id);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
int skynet_socket_udp(struct skynet_context *ctx, const char * addr, int port);
|
||||
int skynet_socket_udp_connect(struct skynet_context *ctx, int id, const char * addr, int port);
|
||||
int skynet_socket_udp_send(struct skynet_context *ctx, int id, const char * address, const void *buffer, int sz);
|
||||
const char * skynet_socket_udp_address(struct skynet_context *ctx, struct skynet_socket_message *, int *addrsz);
|
||||
*/
|
||||
|
||||
static int
|
||||
ludp(lua_State *L) {
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
size_t sz = 0;
|
||||
const char * addr = lua_tolstring(L,1,&sz);
|
||||
char tmp[sz];
|
||||
int port = 0;
|
||||
const char * host = NULL;
|
||||
if (addr) {
|
||||
host = address_port(L, tmp, addr, 2, &port);
|
||||
}
|
||||
|
||||
int id = skynet_socket_udp(ctx, host, port);
|
||||
if (id < 0) {
|
||||
return luaL_error(L, "udp init failed");
|
||||
}
|
||||
lua_pushinteger(L, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ludp_connect(lua_State *L) {
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int id = luaL_checkinteger(L, 1);
|
||||
size_t sz = 0;
|
||||
const char * addr = luaL_checklstring(L,2,&sz);
|
||||
char tmp[sz];
|
||||
int port = 0;
|
||||
const char * host = NULL;
|
||||
if (addr) {
|
||||
host = address_port(L, tmp, addr, 3, &port);
|
||||
}
|
||||
|
||||
if (skynet_socket_udp_connect(ctx, id, host, port)) {
|
||||
return luaL_error(L, "udp connect failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ludp_send(lua_State *L) {
|
||||
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int id = luaL_checkinteger(L, 1);
|
||||
const char * address = luaL_checkstring(L, 2);
|
||||
int sz = 0;
|
||||
void *buffer = get_buffer(L, 3, &sz);
|
||||
int err = skynet_socket_udp_send(ctx, id, address, buffer, sz);
|
||||
|
||||
lua_pushboolean(L, !err);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ludp_address(lua_State *L) {
|
||||
size_t sz = 0;
|
||||
const uint8_t * addr = (const uint8_t *)luaL_checklstring(L, 1, &sz);
|
||||
int port = addr[1] * 256 + addr[2];
|
||||
const void * src = addr+3;
|
||||
char tmp[256];
|
||||
int family;
|
||||
if (sz == 1+2+4) {
|
||||
family = AF_INET;
|
||||
} else {
|
||||
if (sz != 1+2+16) {
|
||||
return luaL_error(L, "Invalid udp address");
|
||||
}
|
||||
family = AF_INET6;
|
||||
}
|
||||
if (inet_ntop(family, src, tmp, sizeof(tmp)) == NULL) {
|
||||
return luaL_error(L, "Invalid udp address");
|
||||
}
|
||||
lua_pushstring(L, tmp);
|
||||
lua_pushinteger(L, port);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_socketdriver(lua_State *L) {
|
||||
@@ -514,6 +646,10 @@ luaopen_socketdriver(lua_State *L) {
|
||||
{ "bind", lbind },
|
||||
{ "start", lstart },
|
||||
{ "nodelay", lnodelay },
|
||||
{ "udp", ludp },
|
||||
{ "udp_connect", ludp_connect },
|
||||
{ "udp_send", ludp_send },
|
||||
{ "udp_address", ludp_address },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
|
||||
|
||||
170
lualib-src/lua_mysqlaux.c
Executable file
170
lualib-src/lua_mysqlaux.c
Executable file
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// lua_mysqlaux.c
|
||||
//
|
||||
// Created by changfeng on 6/17/14.
|
||||
// Copyright (c) 2014 changfeng. All rights reserved.
|
||||
//
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
static unsigned int num_escape_sql_str(unsigned char *dst, unsigned char *src, size_t size)
|
||||
{
|
||||
unsigned int n =0;
|
||||
while (size) {
|
||||
/* the highest bit of all the UTF-8 chars
|
||||
* is always 1 */
|
||||
if ((*src & 0x80) == 0) {
|
||||
switch (*src) {
|
||||
case '\0':
|
||||
case '\b':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case 26: /* \Z */
|
||||
case '\\':
|
||||
case '\'':
|
||||
case '"':
|
||||
n++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
src++;
|
||||
size--;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
static unsigned char*
|
||||
escape_sql_str(unsigned char *dst, unsigned char *src, size_t size)
|
||||
{
|
||||
|
||||
while (size) {
|
||||
if ((*src & 0x80) == 0) {
|
||||
switch (*src) {
|
||||
case '\0':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '0';
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'b';
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'n';
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'r';
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
*dst++ = '\\';
|
||||
*dst++ = 't';
|
||||
break;
|
||||
|
||||
case 26:
|
||||
*dst++ = '\\';
|
||||
*dst++ = 'Z';
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '\\';
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '\'';
|
||||
break;
|
||||
|
||||
case '"':
|
||||
*dst++ = '\\';
|
||||
*dst++ = '"';
|
||||
break;
|
||||
|
||||
default:
|
||||
*dst++ = *src;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*dst++ = *src;
|
||||
}
|
||||
src++;
|
||||
size--;
|
||||
} /* while (size) */
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
quote_sql_str(lua_State *L)
|
||||
{
|
||||
size_t len, dlen, escape;
|
||||
unsigned char *p;
|
||||
unsigned char *src, *dst;
|
||||
|
||||
if (lua_gettop(L) != 1) {
|
||||
return luaL_error(L, "expecting one argument");
|
||||
}
|
||||
|
||||
src = (unsigned char *) luaL_checklstring(L, 1, &len);
|
||||
|
||||
if (len == 0) {
|
||||
dst = (unsigned char *) "''";
|
||||
dlen = sizeof("''") - 1;
|
||||
lua_pushlstring(L, (char *) dst, dlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
escape = num_escape_sql_str(NULL, src, len);
|
||||
|
||||
dlen = sizeof("''") - 1 + len + escape;
|
||||
p = lua_newuserdata(L, dlen);
|
||||
|
||||
dst = p;
|
||||
|
||||
*p++ = '\'';
|
||||
|
||||
if (escape == 0) {
|
||||
memcpy(p, src, len);
|
||||
p+=len;
|
||||
} else {
|
||||
p = (unsigned char *) escape_sql_str(p, src, len);
|
||||
}
|
||||
|
||||
*p++ = '\'';
|
||||
|
||||
if (p != dst + dlen) {
|
||||
return luaL_error(L, "quote sql string error");
|
||||
}
|
||||
|
||||
lua_pushlstring(L, (char *) dst, p - dst);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static struct luaL_Reg mysqlauxlib[] = {
|
||||
{"quote_sql_str",quote_sql_str},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
int luaopen_mysqlaux_c (lua_State *L) {
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, mysqlauxlib, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -19,17 +19,17 @@
|
||||
*/
|
||||
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||
#ifdef luaL_checkversion
|
||||
luaL_checkversion(L);
|
||||
luaL_checkversion(L);
|
||||
#endif
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_setfield(L, -(nup + 2), l->name);
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
lua_setfield(L, -(nup + 2), l->name);
|
||||
}
|
||||
lua_pop(L, nup); /* remove upvalues */
|
||||
}
|
||||
|
||||
#define luaL_newlibtable(L,l) \
|
||||
@@ -62,18 +62,20 @@ ldeleteproto(lua_State *L) {
|
||||
|
||||
static int
|
||||
lquerytype(lua_State *L) {
|
||||
const char * type_name;
|
||||
struct sproto *sp = lua_touserdata(L,1);
|
||||
struct sproto_type *st;
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto object");
|
||||
}
|
||||
const char * typename = luaL_checkstring(L,2);
|
||||
struct sproto_type *st = sproto_type(sp, typename);
|
||||
type_name = luaL_checkstring(L,2);
|
||||
st = sproto_type(sp, type_name);
|
||||
if (st) {
|
||||
lua_pushlightuserdata(L, st);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return luaL_error(L, "type %s not found", typename);
|
||||
return luaL_error(L, "type %s not found", type_name);
|
||||
}
|
||||
|
||||
struct encode_ud {
|
||||
@@ -85,7 +87,7 @@ struct encode_ud {
|
||||
int deep;
|
||||
};
|
||||
|
||||
static int
|
||||
static int
|
||||
encode(void *ud, const char *tagname, int type, int index, struct sproto_type *st, void *value, int length) {
|
||||
struct encode_ud *self = ud;
|
||||
lua_State *L = self->L;
|
||||
@@ -119,9 +121,10 @@ encode(void *ud, const char *tagname, int type, int index, struct sproto_type *s
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
lua_Integer v = luaL_checkinteger(L, -1);
|
||||
lua_Integer vh;
|
||||
lua_pop(L,1);
|
||||
// notice: in lua 5.2, lua_Integer maybe 52bit
|
||||
lua_Integer vh = v >> 31;
|
||||
vh = v >> 31;
|
||||
if (vh == 0 || vh == -1) {
|
||||
*(uint32_t *)value = (uint32_t)v;
|
||||
return 4;
|
||||
@@ -148,13 +151,14 @@ encode(void *ud, const char *tagname, int type, int index, struct sproto_type *s
|
||||
}
|
||||
case SPROTO_TSTRUCT: {
|
||||
struct encode_ud sub;
|
||||
int r;
|
||||
sub.L = L;
|
||||
sub.st = st;
|
||||
sub.tbl_index = lua_gettop(L);
|
||||
sub.array_tag = NULL;
|
||||
sub.array_index = 0;
|
||||
sub.deep = self->deep + 1;
|
||||
int r = sproto_encode(st, value, length, encode, &sub);
|
||||
r = sproto_encode(st, value, length, encode, &sub);
|
||||
lua_pop(L,1);
|
||||
return r;
|
||||
}
|
||||
@@ -165,6 +169,7 @@ encode(void *ud, const char *tagname, int type, int index, struct sproto_type *s
|
||||
|
||||
static void *
|
||||
expand_buffer(lua_State *L, int osz, int nsz) {
|
||||
void *output;
|
||||
do {
|
||||
osz *= 2;
|
||||
} while (osz < nsz);
|
||||
@@ -172,7 +177,7 @@ expand_buffer(lua_State *L, int osz, int nsz) {
|
||||
luaL_error(L, "object is too large (>%d)", ENCODE_MAXSIZE);
|
||||
return NULL;
|
||||
}
|
||||
void *output = lua_newuserdata(L, osz);
|
||||
output = lua_newuserdata(L, osz);
|
||||
lua_replace(L, lua_upvalueindex(1));
|
||||
lua_pushinteger(L, osz);
|
||||
lua_replace(L, lua_upvalueindex(2));
|
||||
@@ -184,10 +189,11 @@ expand_buffer(lua_State *L, int osz, int nsz) {
|
||||
lightuserdata sproto_type
|
||||
table source
|
||||
|
||||
return string
|
||||
return string
|
||||
*/
|
||||
static int
|
||||
lencode(lua_State *L) {
|
||||
struct encode_ud self;
|
||||
void * buffer = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int sz = lua_tointeger(L, lua_upvalueindex(2));
|
||||
|
||||
@@ -197,7 +203,6 @@ lencode(lua_State *L) {
|
||||
}
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
luaL_checkstack(L, ENCODE_DEEPLEVEL + 8, NULL);
|
||||
struct encode_ud self;
|
||||
self.L = L;
|
||||
self.st = st;
|
||||
self.tbl_index = 2;
|
||||
@@ -224,7 +229,7 @@ struct decode_ud {
|
||||
int deep;
|
||||
};
|
||||
|
||||
static int
|
||||
static int
|
||||
decode(void *ud, const char *tagname, int type, int index, struct sproto_type *st, void *value, int length) {
|
||||
struct decode_ud * self = ud;
|
||||
lua_State *L = self->L;
|
||||
@@ -247,12 +252,12 @@ decode(void *ud, const char *tagname, int type, int index, struct sproto_type *s
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
// notice: in lua 5.2, 52bit integer support (not 64)
|
||||
lua_Integer v = *(lua_Integer *)value;
|
||||
lua_Integer v = *(uint64_t*)value;
|
||||
lua_pushinteger(L, v);
|
||||
break;
|
||||
}
|
||||
case SPROTO_TBOOLEAN: {
|
||||
int v = *(lua_Integer*)value;
|
||||
int v = *(uint64_t*)value;
|
||||
lua_pushboolean(L,v);
|
||||
break;
|
||||
}
|
||||
@@ -261,15 +266,16 @@ decode(void *ud, const char *tagname, int type, int index, struct sproto_type *s
|
||||
break;
|
||||
}
|
||||
case SPROTO_TSTRUCT: {
|
||||
lua_newtable(L);
|
||||
struct decode_ud sub;
|
||||
int r;
|
||||
lua_newtable(L);
|
||||
sub.L = L;
|
||||
sub.result_index = lua_gettop(L);
|
||||
sub.deep = self->deep + 1;
|
||||
sub.array_index = 0;
|
||||
sub.array_tag = NULL;
|
||||
|
||||
int r = sproto_decode(st, value, length, decode, &sub);
|
||||
r = sproto_decode(st, value, length, decode, &sub);
|
||||
if (r < 0 || r != length)
|
||||
return r;
|
||||
lua_settop(L, sub.result_index);
|
||||
@@ -312,22 +318,25 @@ getbuffer(lua_State *L, int index, size_t *sz) {
|
||||
static int
|
||||
ldecode(lua_State *L) {
|
||||
struct sproto_type * st = lua_touserdata(L, 1);
|
||||
const void * buffer;
|
||||
struct decode_ud self;
|
||||
size_t sz;
|
||||
int r;
|
||||
if (st == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
size_t sz=0;
|
||||
const void * buffer = getbuffer(L, 2, &sz);
|
||||
sz = 0;
|
||||
buffer = getbuffer(L, 2, &sz);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
}
|
||||
luaL_checkstack(L, ENCODE_DEEPLEVEL*2 + 8, NULL);
|
||||
struct decode_ud self;
|
||||
self.L = L;
|
||||
self.result_index = lua_gettop(L);
|
||||
self.array_index = 0;
|
||||
self.array_tag = NULL;
|
||||
self.deep = 0;
|
||||
int r = sproto_decode(st, buffer, (int)sz, decode, &self);
|
||||
r = sproto_decode(st, buffer, (int)sz, decode, &self);
|
||||
if (r < 0) {
|
||||
return luaL_error(L, "decode error");
|
||||
}
|
||||
@@ -359,11 +368,12 @@ lpack(lua_State *L) {
|
||||
// the worst-case space overhead of packing is 2 bytes per 2 KiB of input (256 words = 2KiB).
|
||||
size_t maxsz = (sz + 2047) / 2048 * 2 + sz;
|
||||
void * output = lua_touserdata(L, lua_upvalueindex(1));
|
||||
int bytes;
|
||||
int osz = lua_tointeger(L, lua_upvalueindex(2));
|
||||
if (osz < maxsz) {
|
||||
output = expand_buffer(L, osz, maxsz);
|
||||
}
|
||||
int bytes = sproto_pack(buffer, sz, output, maxsz);
|
||||
bytes = sproto_pack(buffer, sz, output, maxsz);
|
||||
if (bytes > maxsz) {
|
||||
return luaL_error(L, "packing error, return size = %d", bytes);
|
||||
}
|
||||
@@ -402,14 +412,18 @@ pushfunction_withbuffer(lua_State *L, const char * name, lua_CFunction func) {
|
||||
static int
|
||||
lprotocol(lua_State *L) {
|
||||
struct sproto * sp = lua_touserdata(L, 1);
|
||||
struct sproto_type * request;
|
||||
struct sproto_type * response;
|
||||
int t;
|
||||
int tag;
|
||||
if (sp == NULL) {
|
||||
return luaL_argerror(L, 1, "Need a sproto_type object");
|
||||
}
|
||||
int t = lua_type(L,2);
|
||||
int tag;
|
||||
t = lua_type(L,2);
|
||||
if (t == LUA_TNUMBER) {
|
||||
const char * name;
|
||||
tag = lua_tointeger(L, 2);
|
||||
const char * name = sproto_protoname(sp, tag);
|
||||
name = sproto_protoname(sp, tag);
|
||||
if (name == NULL)
|
||||
return 0;
|
||||
lua_pushstring(L, name);
|
||||
@@ -420,13 +434,13 @@ lprotocol(lua_State *L) {
|
||||
return 0;
|
||||
lua_pushinteger(L, tag);
|
||||
}
|
||||
struct sproto_type * request = sproto_protoquery(sp, tag, SPROTO_REQUEST);
|
||||
request = sproto_protoquery(sp, tag, SPROTO_REQUEST);
|
||||
if (request == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlightuserdata(L, request);
|
||||
}
|
||||
struct sproto_type * response = sproto_protoquery(sp, tag, SPROTO_RESPONSE);
|
||||
response = sproto_protoquery(sp, tag, SPROTO_RESPONSE);
|
||||
if (response == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
|
||||
@@ -119,8 +119,8 @@ todword(const uint8_t *p) {
|
||||
static int
|
||||
count_array(const uint8_t * stream) {
|
||||
uint32_t length = todword(stream);
|
||||
stream += SIZEOF_LENGTH;
|
||||
int n = 0;
|
||||
stream += SIZEOF_LENGTH;
|
||||
while (length > 0) {
|
||||
uint32_t nsz;
|
||||
if (length < SIZEOF_LENGTH)
|
||||
@@ -180,6 +180,10 @@ static const uint8_t *
|
||||
import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
uint32_t sz;
|
||||
const uint8_t * result;
|
||||
int fn;
|
||||
int i;
|
||||
int array = 0;
|
||||
int tag = -1;
|
||||
f->tag = -1;
|
||||
f->type = -1;
|
||||
f->name = NULL;
|
||||
@@ -188,13 +192,10 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
sz = todword(stream);
|
||||
stream += SIZEOF_LENGTH;
|
||||
result = stream + sz;
|
||||
int fn = struct_field(stream, sz);
|
||||
fn = struct_field(stream, sz);
|
||||
if (fn < 0)
|
||||
return NULL;
|
||||
stream += SIZEOF_HEADER;
|
||||
int i;
|
||||
int array = 0;
|
||||
int tag = -1;
|
||||
for (i=0;i<fn;i++) {
|
||||
int value;
|
||||
++tag;
|
||||
@@ -203,7 +204,7 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
tag+= value/2;
|
||||
continue;
|
||||
}
|
||||
if (tag == 0) { // name
|
||||
if (tag == 0) { // name
|
||||
if (value != 0)
|
||||
return NULL;
|
||||
f->name = import_string(s, stream + fn * SIZEOF_FIELD);
|
||||
@@ -213,12 +214,12 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
return NULL;
|
||||
value = value/2 - 1;
|
||||
switch(tag) {
|
||||
case 1: // buildin
|
||||
case 1: // buildin
|
||||
if (value >= SPROTO_TSTRUCT)
|
||||
return NULL; // invalid buildin type
|
||||
f->type = value;
|
||||
break;
|
||||
case 2: // type index
|
||||
case 2: // type index
|
||||
if (value >= s->type_n)
|
||||
return NULL; // invalid type index
|
||||
if (f->type >= 0)
|
||||
@@ -226,10 +227,10 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
f->type = SPROTO_TSTRUCT;
|
||||
f->st = &s->type[value];
|
||||
break;
|
||||
case 3: // tag
|
||||
case 3: // tag
|
||||
f->tag = value;
|
||||
break;
|
||||
case 4: // array
|
||||
case 4: // array
|
||||
if (value)
|
||||
array = SPROTO_TARRAY;
|
||||
break;
|
||||
@@ -248,10 +249,10 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
.type {
|
||||
.field {
|
||||
name 0 : string
|
||||
buildin 1 : integer
|
||||
buildin 1 : integer
|
||||
type 2 : integer
|
||||
tag 3 : integer
|
||||
array 4 : boolean
|
||||
tag 3 : integer
|
||||
array 4 : boolean
|
||||
}
|
||||
name 0 : string
|
||||
fields 1 : *field
|
||||
@@ -259,11 +260,16 @@ import_field(struct sproto *s, struct field *f, const uint8_t * stream) {
|
||||
*/
|
||||
static const uint8_t *
|
||||
import_type(struct sproto *s, struct sproto_type *t, const uint8_t * stream) {
|
||||
const uint8_t * result;
|
||||
uint32_t sz = todword(stream);
|
||||
int i;
|
||||
int fn;
|
||||
int n;
|
||||
int maxn;
|
||||
int last;
|
||||
stream += SIZEOF_LENGTH;
|
||||
const uint8_t * result = stream + sz;
|
||||
int fn = struct_field(stream, sz);
|
||||
result = stream + sz;
|
||||
fn = struct_field(stream, sz);
|
||||
if (fn <= 0 || fn > 2)
|
||||
return NULL;
|
||||
for (i=0;i<fn*SIZEOF_FIELD;i+=SIZEOF_FIELD) {
|
||||
@@ -279,20 +285,21 @@ import_type(struct sproto *s, struct sproto_type *t, const uint8_t * stream) {
|
||||
return result;
|
||||
}
|
||||
stream += todword(stream)+SIZEOF_LENGTH; // second data
|
||||
int n = count_array(stream);
|
||||
n = count_array(stream);
|
||||
if (n<0)
|
||||
return NULL;
|
||||
stream += SIZEOF_LENGTH;
|
||||
int maxn = n;
|
||||
int last = -1;
|
||||
maxn = n;
|
||||
last = -1;
|
||||
t->n = n;
|
||||
t->f = pool_alloc(&s->memory, sizeof(struct field) * n);
|
||||
for (i=0;i<n;i++) {
|
||||
int tag;
|
||||
struct field *f = &t->f[i];
|
||||
stream = import_field(s, f, stream);
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
int tag = f->tag;
|
||||
tag = f->tag;
|
||||
if (tag < last)
|
||||
return NULL; // tag must in ascending order
|
||||
if (tag > last+1) {
|
||||
@@ -312,24 +319,27 @@ import_type(struct sproto *s, struct sproto_type *t, const uint8_t * stream) {
|
||||
/*
|
||||
.protocol {
|
||||
name 0 : string
|
||||
tag 1 : integer
|
||||
request 2 : integer
|
||||
tag 1 : integer
|
||||
request 2 : integer
|
||||
response 3 : integer
|
||||
}
|
||||
*/
|
||||
static const uint8_t *
|
||||
import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
|
||||
const uint8_t * result;
|
||||
uint32_t sz = todword(stream);
|
||||
int fn;
|
||||
int i;
|
||||
int tag;
|
||||
stream += SIZEOF_LENGTH;
|
||||
const uint8_t * result = stream + sz;
|
||||
int fn = struct_field(stream, sz);
|
||||
result = stream + sz;
|
||||
fn = struct_field(stream, sz);
|
||||
stream += SIZEOF_HEADER;
|
||||
p->name = NULL;
|
||||
p->tag = -1;
|
||||
p->p[SPROTO_REQUEST] = NULL;
|
||||
p->p[SPROTO_RESPONSE] = NULL;
|
||||
int i;
|
||||
int tag = 0;
|
||||
tag = 0;
|
||||
for (i=0;i<fn;i++,tag++) {
|
||||
int value = toword(stream + SIZEOF_FIELD * i);
|
||||
if (value & 1) {
|
||||
@@ -338,24 +348,24 @@ import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
|
||||
}
|
||||
value = value/2 - 1;
|
||||
switch (i) {
|
||||
case 0: // name
|
||||
case 0: // name
|
||||
if (value != -1) {
|
||||
return NULL;
|
||||
}
|
||||
p->name = import_string(s, stream + SIZEOF_FIELD *fn);
|
||||
break;
|
||||
case 1: // tag
|
||||
case 1: // tag
|
||||
if (value < 0) {
|
||||
return NULL;
|
||||
}
|
||||
p->tag = value;
|
||||
break;
|
||||
case 2: // request
|
||||
case 2: // request
|
||||
if (value < 0 || value>=s->type_n)
|
||||
return NULL;
|
||||
p->p[SPROTO_REQUEST] = &s->type[value];
|
||||
break;
|
||||
case 3: // response
|
||||
case 3: // response
|
||||
if (value < 0 || value>=s->type_n)
|
||||
return NULL;
|
||||
p->p[SPROTO_RESPONSE] = &s->type[value];
|
||||
@@ -374,22 +384,23 @@ import_protocol(struct sproto *s, struct protocol *p, const uint8_t * stream) {
|
||||
|
||||
static struct sproto *
|
||||
create_from_bundle(struct sproto *s, const uint8_t * stream, size_t sz) {
|
||||
const uint8_t * content;
|
||||
const uint8_t * typedata = NULL;
|
||||
const uint8_t * protocoldata = NULL;
|
||||
int fn = struct_field(stream, sz);
|
||||
int i;
|
||||
if (fn < 0)
|
||||
return NULL;
|
||||
|
||||
stream += SIZEOF_HEADER;
|
||||
content = stream + fn*SIZEOF_FIELD;
|
||||
|
||||
const uint8_t * content = stream + fn*SIZEOF_FIELD;
|
||||
const uint8_t * typedata = NULL;
|
||||
const uint8_t * protocoldata = NULL;
|
||||
|
||||
int i;
|
||||
for (i=0;i<fn;i++) {
|
||||
int value = toword(stream + i*SIZEOF_FIELD);
|
||||
int n;
|
||||
if (value != 0)
|
||||
return NULL;
|
||||
int n = count_array(content);
|
||||
n = count_array(content);
|
||||
if (n<0)
|
||||
return NULL;
|
||||
if (i == 0) {
|
||||
@@ -423,8 +434,9 @@ create_from_bundle(struct sproto *s, const uint8_t * stream, size_t sz) {
|
||||
struct sproto *
|
||||
sproto_create(const void * proto, size_t sz) {
|
||||
struct pool mem;
|
||||
struct sproto * s;
|
||||
pool_init(&mem);
|
||||
struct sproto * s = pool_alloc(&mem, sizeof(*s));
|
||||
s = pool_alloc(&mem, sizeof(*s));
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
memset(s, 0, sizeof(*s));
|
||||
@@ -445,34 +457,35 @@ sproto_release(struct sproto * s) {
|
||||
|
||||
void
|
||||
sproto_dump(struct sproto *s) {
|
||||
int i,j;
|
||||
static const char * buildin[] = {
|
||||
"integer",
|
||||
"boolean",
|
||||
"string",
|
||||
};
|
||||
printf("=== %d types ===\n", s->type_n);
|
||||
int i,j;
|
||||
for (i=0;i<s->type_n;i++) {
|
||||
struct sproto_type *t = &s->type[i];
|
||||
printf("%s\n", t->name);
|
||||
for (j=0;j<t->n;j++) {
|
||||
char array[2] = { 0, 0 };
|
||||
const char * typename = NULL;
|
||||
const char * type_name = NULL;
|
||||
struct field *f = &t->f[j];
|
||||
if (f->type & SPROTO_TARRAY) {
|
||||
array[0] = '*';
|
||||
} else {
|
||||
array[0] = 0;
|
||||
}
|
||||
int t = f->type & ~SPROTO_TARRAY;
|
||||
if (t == SPROTO_TSTRUCT) {
|
||||
typename = f->st->name;
|
||||
} else {
|
||||
assert(t<SPROTO_TSTRUCT);
|
||||
typename = buildin[t];
|
||||
{
|
||||
int t = f->type & ~SPROTO_TARRAY;
|
||||
if (t == SPROTO_TSTRUCT) {
|
||||
type_name = f->st->name;
|
||||
} else {
|
||||
assert(t<SPROTO_TSTRUCT);
|
||||
type_name = buildin[t];
|
||||
}
|
||||
}
|
||||
|
||||
printf("\t%s (%d) %s%s\n", f->name, f->tag, array, typename);
|
||||
printf("\t%s (%d) %s%s\n", f->name, f->tag, array, type_name);
|
||||
}
|
||||
}
|
||||
printf("=== %d protocol ===\n", s->protocol_n);
|
||||
@@ -487,7 +500,7 @@ sproto_dump(struct sproto *s) {
|
||||
}
|
||||
|
||||
// query
|
||||
int
|
||||
int
|
||||
sproto_prototag(struct sproto *sp, const char * name) {
|
||||
int i;
|
||||
for (i=0;i<sp->protocol_n;i++) {
|
||||
@@ -516,19 +529,20 @@ query_proto(struct sproto *sp, int tag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sproto_type *
|
||||
struct sproto_type *
|
||||
sproto_protoquery(struct sproto *sp, int proto, int what) {
|
||||
struct protocol * p;
|
||||
if (what <0 || what >1) {
|
||||
return NULL;
|
||||
}
|
||||
struct protocol * p = query_proto(sp, proto);
|
||||
p = query_proto(sp, proto);
|
||||
if (p) {
|
||||
return p->p[what];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
const char *
|
||||
sproto_protoname(struct sproto *sp, int proto) {
|
||||
struct protocol * p = query_proto(sp, proto);
|
||||
if (p) {
|
||||
@@ -537,7 +551,7 @@ sproto_protoname(struct sproto *sp, int proto) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sproto_type *
|
||||
struct sproto_type *
|
||||
sproto_type(struct sproto *sp, const char * type_name) {
|
||||
int i;
|
||||
for (i=0;i<sp->type_n;i++) {
|
||||
@@ -555,13 +569,15 @@ sproto_name(struct sproto_type * st) {
|
||||
|
||||
static struct field *
|
||||
findtag(struct sproto_type *st, int tag) {
|
||||
int begin, end;
|
||||
if (st->base >=0 ) {
|
||||
tag -= st->base;
|
||||
if (tag < 0 || tag >= st->n)
|
||||
return NULL;
|
||||
return &st->f[tag];
|
||||
}
|
||||
int begin = 0, end = st->n;
|
||||
begin = 0;
|
||||
end = st->n;
|
||||
while (begin < end) {
|
||||
int mid = (begin+end)/2;
|
||||
struct field *f = &st->f[mid];
|
||||
@@ -580,7 +596,7 @@ findtag(struct sproto_type *st, int tag) {
|
||||
|
||||
// encode & decode
|
||||
// sproto_callback(void *ud, int tag, int type, struct sproto_type *, void *value, int length)
|
||||
// return size, -1 means error
|
||||
// return size, -1 means error
|
||||
|
||||
static inline int
|
||||
fill_size(uint8_t * data, int sz) {
|
||||
@@ -623,18 +639,20 @@ encode_uint64(uint64_t v, uint8_t * data, int size) {
|
||||
|
||||
static int
|
||||
encode_string(sproto_callback cb, void *ud, struct field *f, uint8_t *data, int size) {
|
||||
int sz;
|
||||
if (size < SIZEOF_LENGTH)
|
||||
return -1;
|
||||
int sz = cb(ud, f->name, SPROTO_TSTRING, 0, NULL, data+SIZEOF_LENGTH, size-SIZEOF_LENGTH);
|
||||
sz = cb(ud, f->name, SPROTO_TSTRING, 0, NULL, data+SIZEOF_LENGTH, size-SIZEOF_LENGTH);
|
||||
return fill_size(data, sz);
|
||||
}
|
||||
|
||||
static int
|
||||
encode_struct(sproto_callback cb, void *ud, struct field *f, uint8_t *data, int size) {
|
||||
int sz;
|
||||
if (size < SIZEOF_LENGTH) {
|
||||
return -1;
|
||||
}
|
||||
int sz = cb(ud, f->name, SPROTO_TSTRUCT, 0, f->st, data+SIZEOF_LENGTH, size-SIZEOF_LENGTH);
|
||||
sz = cb(ud, f->name, SPROTO_TSTRUCT, 0, f->st, data+SIZEOF_LENGTH, size-SIZEOF_LENGTH);
|
||||
return fill_size(data, sz);
|
||||
}
|
||||
|
||||
@@ -656,18 +674,21 @@ uint32_to_uint64(int negative, uint8_t *buffer) {
|
||||
static uint8_t *
|
||||
encode_integer_array(sproto_callback cb, void *ud, struct field *f, uint8_t *buffer, int size) {
|
||||
uint8_t * header = buffer;
|
||||
int intlen;
|
||||
int index;
|
||||
if (size < 1)
|
||||
return NULL;
|
||||
buffer++;
|
||||
size--;
|
||||
int intlen = sizeof(uint32_t);
|
||||
int index = 1;
|
||||
intlen = sizeof(uint32_t);
|
||||
index = 1;
|
||||
for (;;) {
|
||||
int sz;
|
||||
union {
|
||||
uint64_t u64;
|
||||
uint32_t u32;
|
||||
} u;
|
||||
int sz = cb(ud, f->name, SPROTO_TINTEGER, index, f->st, &u, sizeof(u));
|
||||
sz = cb(ud, f->name, SPROTO_TINTEGER, index, f->st, &u, sizeof(u));
|
||||
if (sz < 0)
|
||||
return NULL;
|
||||
if (sz == 0)
|
||||
@@ -685,24 +706,26 @@ encode_integer_array(sproto_callback cb, void *ud, struct field *f, uint8_t *buf
|
||||
uint32_to_uint64(v & 0x80000000, buffer);
|
||||
}
|
||||
} else {
|
||||
uint64_t v;
|
||||
if (sz != sizeof(uint64_t))
|
||||
return NULL;
|
||||
if (intlen == sizeof(uint32_t)) {
|
||||
int i;
|
||||
// rearrange
|
||||
size -= (index-1) * sizeof(uint32_t);
|
||||
if (size < sizeof(uint64_t))
|
||||
return NULL;
|
||||
buffer += (index-1) * sizeof(uint32_t);
|
||||
int i;
|
||||
for (i=index-2;i>=0;i--) {
|
||||
int negative;
|
||||
memcpy(header+1+i*sizeof(uint64_t), header+1+i*sizeof(uint32_t), sizeof(uint32_t));
|
||||
int negative = header[1+i*sizeof(uint64_t)+3] & 0x80;
|
||||
negative = header[1+i*sizeof(uint64_t)+3] & 0x80;
|
||||
uint32_to_uint64(negative, header+1+i*sizeof(uint64_t));
|
||||
}
|
||||
intlen = sizeof(uint64_t);
|
||||
}
|
||||
|
||||
uint64_t v = u.u64;
|
||||
v = u.u64;
|
||||
buffer[0] = v & 0xff;
|
||||
buffer[1] = (v >> 8) & 0xff;
|
||||
buffer[2] = (v >> 16) & 0xff;
|
||||
@@ -727,12 +750,16 @@ encode_integer_array(sproto_callback cb, void *ud, struct field *f, uint8_t *buf
|
||||
|
||||
static int
|
||||
encode_array(sproto_callback cb, void *ud, struct field *f, uint8_t *data, int size) {
|
||||
uint8_t * buffer;
|
||||
int index;
|
||||
int type;
|
||||
int sz;
|
||||
if (size < SIZEOF_LENGTH)
|
||||
return -1;
|
||||
size -= SIZEOF_LENGTH;
|
||||
int index = 1;
|
||||
uint8_t * buffer = data + SIZEOF_LENGTH;
|
||||
int type = f->type & ~SPROTO_TARRAY;
|
||||
index = 1;
|
||||
buffer = data + SIZEOF_LENGTH;
|
||||
type = f->type & ~SPROTO_TARRAY;
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER:
|
||||
buffer = encode_integer_array(cb,ud,f,buffer,size);
|
||||
@@ -757,10 +784,11 @@ encode_array(sproto_callback cb, void *ud, struct field *f, uint8_t *data, int s
|
||||
break;
|
||||
default:
|
||||
for (;;) {
|
||||
int sz;
|
||||
if (size < SIZEOF_LENGTH)
|
||||
return -1;
|
||||
size -= SIZEOF_LENGTH;
|
||||
int sz = cb(ud, f->name, type, index, f->st, buffer+SIZEOF_LENGTH, size);
|
||||
sz = cb(ud, f->name, type, index, f->st, buffer+SIZEOF_LENGTH, size);
|
||||
if (sz < 0)
|
||||
return -1;
|
||||
if (sz == 0)
|
||||
@@ -772,24 +800,27 @@ encode_array(sproto_callback cb, void *ud, struct field *f, uint8_t *data, int s
|
||||
}
|
||||
break;
|
||||
}
|
||||
int sz = buffer - (data + SIZEOF_LENGTH);
|
||||
sz = buffer - (data + SIZEOF_LENGTH);
|
||||
if (sz == 0)
|
||||
return 0;
|
||||
return fill_size(data, sz);
|
||||
}
|
||||
|
||||
int
|
||||
int
|
||||
sproto_encode(struct sproto_type *st, void * buffer, int size, sproto_callback cb, void *ud) {
|
||||
uint8_t * header = buffer;
|
||||
uint8_t * data;
|
||||
int header_sz = SIZEOF_HEADER + st->maxn * SIZEOF_FIELD;
|
||||
int i;
|
||||
int index;
|
||||
int lasttag;
|
||||
int datasz;
|
||||
if (size < header_sz)
|
||||
return -1;
|
||||
data = header + header_sz;
|
||||
size -= header_sz;
|
||||
int i;
|
||||
int index = 0;
|
||||
int lasttag = -1;
|
||||
index = 0;
|
||||
lasttag = -1;
|
||||
for (i=0;i<st->n;i++) {
|
||||
struct field *f = &st->f[i];
|
||||
int type = f->type;
|
||||
@@ -799,7 +830,7 @@ sproto_encode(struct sproto_type *st, void * buffer, int size, sproto_callback c
|
||||
sz = encode_array(cb,ud, f, data, size);
|
||||
} else {
|
||||
switch(type) {
|
||||
case SPROTO_TINTEGER:
|
||||
case SPROTO_TINTEGER:
|
||||
case SPROTO_TBOOLEAN: {
|
||||
union {
|
||||
uint64_t u64;
|
||||
@@ -813,7 +844,7 @@ sproto_encode(struct sproto_type *st, void * buffer, int size, sproto_callback c
|
||||
if (sz == sizeof(uint32_t)) {
|
||||
if (u.u32 < 0x7fff) {
|
||||
value = (u.u32+1) * 2;
|
||||
sz = 2; // sz can be any number > 0
|
||||
sz = 2; // sz can be any number > 0
|
||||
} else {
|
||||
sz = encode_integer(u.u32, data, size);
|
||||
}
|
||||
@@ -837,12 +868,14 @@ sproto_encode(struct sproto_type *st, void * buffer, int size, sproto_callback c
|
||||
if (sz < 0)
|
||||
return -1;
|
||||
if (sz > 0) {
|
||||
uint8_t * record;
|
||||
int tag;
|
||||
if (value == 0) {
|
||||
data += sz;
|
||||
size -= sz;
|
||||
}
|
||||
uint8_t * record = header+SIZEOF_HEADER+SIZEOF_FIELD*index;
|
||||
int tag = f->tag - lasttag - 1;
|
||||
record = header+SIZEOF_HEADER+SIZEOF_FIELD*index;
|
||||
tag = f->tag - lasttag - 1;
|
||||
if (tag > 0) {
|
||||
// skip tag
|
||||
tag = (tag - 1) * 2 + 1;
|
||||
@@ -862,7 +895,7 @@ sproto_encode(struct sproto_type *st, void * buffer, int size, sproto_callback c
|
||||
header[0] = index & 0xff;
|
||||
header[1] = (index >> 8) & 0xff;
|
||||
|
||||
int datasz = data - (header + header_sz);
|
||||
datasz = data - (header + header_sz);
|
||||
data = header + header_sz;
|
||||
if (index != st->maxn) {
|
||||
memmove(header + SIZEOF_HEADER + index * SIZEOF_FIELD, data, datasz);
|
||||
@@ -909,9 +942,10 @@ decode_array(sproto_callback cb, void *ud, struct field *f, uint8_t * stream) {
|
||||
stream += SIZEOF_LENGTH;
|
||||
switch (type) {
|
||||
case SPROTO_TINTEGER: {
|
||||
int len;
|
||||
if (sz < 1)
|
||||
return -1;
|
||||
int len = *stream;
|
||||
len = *stream;
|
||||
++stream;
|
||||
--sz;
|
||||
if (len == sizeof(uint32_t)) {
|
||||
@@ -937,7 +971,7 @@ decode_array(sproto_callback cb, void *ud, struct field *f, uint8_t * stream) {
|
||||
}
|
||||
case SPROTO_TBOOLEAN:
|
||||
for (i=0;i<sz;i++) {
|
||||
int value = stream[i];
|
||||
uint64_t value = stream[i];
|
||||
cb(ud, f->name, SPROTO_TBOOLEAN, i+1, NULL, &value, sizeof(value));
|
||||
}
|
||||
break;
|
||||
@@ -956,6 +990,8 @@ sproto_decode(struct sproto_type *st, const void * data, int size, sproto_callba
|
||||
uint8_t * stream;
|
||||
uint8_t * datastream;
|
||||
int fn;
|
||||
int i;
|
||||
int tag;
|
||||
if (size < SIZEOF_HEADER)
|
||||
return -1;
|
||||
stream = (void *)data;
|
||||
@@ -967,17 +1003,18 @@ sproto_decode(struct sproto_type *st, const void * data, int size, sproto_callba
|
||||
datastream = stream + fn * SIZEOF_FIELD;
|
||||
size -= fn * SIZEOF_FIELD ;
|
||||
|
||||
int i;
|
||||
int tag = -1;
|
||||
tag = -1;
|
||||
for (i=0;i<fn;i++) {
|
||||
++ tag;
|
||||
uint8_t * currentdata;
|
||||
struct field * f;
|
||||
int value = toword(stream + i * SIZEOF_FIELD);
|
||||
++ tag;
|
||||
if (value & 1) {
|
||||
tag += value/2;
|
||||
continue;
|
||||
}
|
||||
value = value/2 - 1;
|
||||
uint8_t * currentdata = datastream;
|
||||
currentdata = datastream;
|
||||
if (value < 0) {
|
||||
uint32_t sz;
|
||||
if (size < SIZEOF_LENGTH)
|
||||
@@ -988,7 +1025,7 @@ sproto_decode(struct sproto_type *st, const void * data, int size, sproto_callba
|
||||
datastream += sz+SIZEOF_LENGTH;
|
||||
size -= sz+SIZEOF_LENGTH;
|
||||
}
|
||||
struct field * f= findtag(st, tag);
|
||||
f = findtag(st, tag);
|
||||
if (f == NULL)
|
||||
continue;
|
||||
if (value < 0) {
|
||||
@@ -1013,7 +1050,7 @@ sproto_decode(struct sproto_type *st, const void * data, int size, sproto_callba
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPROTO_TSTRING:
|
||||
case SPROTO_TSTRING:
|
||||
case SPROTO_TSTRUCT: {
|
||||
uint32_t sz = todword(currentdata);
|
||||
if (cb(ud, f->name, f->type, 0, f->st, currentdata+SIZEOF_LENGTH, sz))
|
||||
@@ -1076,12 +1113,18 @@ pack_seg(const uint8_t *src, uint8_t * buffer, int sz, int n) {
|
||||
|
||||
static inline void
|
||||
write_ff(const uint8_t * src, uint8_t * des, int n) {
|
||||
int i;
|
||||
int align8_n = (n+7)&(~7);
|
||||
|
||||
des[0] = 0xff;
|
||||
des[1] = n-1;
|
||||
memcpy(des+2, src, n * 8);
|
||||
des[1] = align8_n/8 - 1;
|
||||
memcpy(des+2, src, n);
|
||||
for(i=0; i< align8_n-n; i++){
|
||||
des[n+2+i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
int
|
||||
sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
uint8_t tmp[8];
|
||||
int i;
|
||||
@@ -1092,6 +1135,7 @@ sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
const uint8_t * src = srcv;
|
||||
uint8_t * buffer = bufferv;
|
||||
for (i=0;i<srcsz;i+=8) {
|
||||
int n;
|
||||
int padding = i+8 - srcsz;
|
||||
if (padding > 0) {
|
||||
int j;
|
||||
@@ -1101,7 +1145,7 @@ sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
}
|
||||
src = tmp;
|
||||
}
|
||||
int n = pack_seg(src, buffer, bufsz, ff_n);
|
||||
n = pack_seg(src, buffer, bufsz, ff_n);
|
||||
bufsz -= n;
|
||||
if (n == 10) {
|
||||
// first FF
|
||||
@@ -1112,14 +1156,14 @@ sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
++ff_n;
|
||||
if (ff_n == 256) {
|
||||
if (bufsz >= 0) {
|
||||
write_ff(ff_srcstart, ff_desstart, 256);
|
||||
write_ff(ff_srcstart, ff_desstart, 256*8);
|
||||
}
|
||||
ff_n = 0;
|
||||
}
|
||||
} else {
|
||||
if (ff_n > 0) {
|
||||
if (bufsz >= 0) {
|
||||
write_ff(ff_srcstart, ff_desstart, ff_n);
|
||||
write_ff(ff_srcstart, ff_desstart, ff_n*8);
|
||||
}
|
||||
ff_n = 0;
|
||||
}
|
||||
@@ -1128,13 +1172,16 @@ sproto_pack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
buffer += n;
|
||||
size += n;
|
||||
}
|
||||
if (ff_n > 0 && bufsz >= 0) {
|
||||
write_ff(ff_srcstart, ff_desstart, ff_n);
|
||||
if(bufsz >= 0){
|
||||
if(ff_n == 1)
|
||||
write_ff(ff_srcstart, ff_desstart, 8);
|
||||
else if (ff_n > 1)
|
||||
write_ff(ff_srcstart, ff_desstart, srcsz - (intptr_t)(ff_srcstart - (const uint8_t*)srcv));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int
|
||||
int
|
||||
sproto_unpack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
const uint8_t * src = srcv;
|
||||
uint8_t * buffer = bufferv;
|
||||
@@ -1144,10 +1191,11 @@ sproto_unpack(const void * srcv, int srcsz, void * bufferv, int bufsz) {
|
||||
--srcsz;
|
||||
++src;
|
||||
if (header == 0xff) {
|
||||
int n;
|
||||
if (srcsz < 0) {
|
||||
return -1;
|
||||
}
|
||||
int n = (src[0] + 1) * 8;
|
||||
n = (src[0] + 1) * 8;
|
||||
if (srcsz < n + 1)
|
||||
return -1;
|
||||
srcsz -= n + 1;
|
||||
|
||||
@@ -81,10 +81,10 @@ function httpc.request(method, host, url, recvheader, header, content)
|
||||
end
|
||||
local fd = socket.connect(hostname, port)
|
||||
local ok , statuscode, body = pcall(request, fd,method, host, url, recvheader, header, content)
|
||||
socket.close(fd)
|
||||
if ok then
|
||||
return statuscode, body
|
||||
else
|
||||
socket.close(fd)
|
||||
error(statuscode)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ local function readall(readbytes, bodylimit)
|
||||
else
|
||||
-- identity mode
|
||||
if length then
|
||||
if length > bodylimit then
|
||||
if bodylimit and length > bodylimit then
|
||||
return 413
|
||||
end
|
||||
if #body >= length then
|
||||
@@ -130,6 +130,7 @@ local function writeall(writefunc, statuscode, bodyfunc, header)
|
||||
end
|
||||
else
|
||||
writefunc("\r\n0\r\n\r\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -4,14 +4,15 @@ local LIMIT = 8192
|
||||
|
||||
local function chunksize(readbytes, body)
|
||||
while true do
|
||||
if #body > 128 then
|
||||
return
|
||||
end
|
||||
body = body .. readbytes()
|
||||
local f,e = body:find("\r\n",1,true)
|
||||
if f then
|
||||
return tonumber(body:sub(1,f-1),16), body:sub(e+1)
|
||||
end
|
||||
if #body > 128 then
|
||||
-- pervent the attacker send very long stream without \r\n
|
||||
return
|
||||
end
|
||||
body = body .. readbytes()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -120,6 +120,8 @@ function mongo.client( conf )
|
||||
local obj = {
|
||||
host = first.host,
|
||||
port = first.port or 27017,
|
||||
username = first.username,
|
||||
password = first.password,
|
||||
}
|
||||
|
||||
obj.__id = 0
|
||||
@@ -129,6 +131,7 @@ function mongo.client( conf )
|
||||
response = dispatch_reply,
|
||||
auth = mongo_auth(obj),
|
||||
backup = backup,
|
||||
nodelay = true,
|
||||
}
|
||||
setmetatable(obj, client_meta)
|
||||
obj.__sock:connect(true) -- try connect only once
|
||||
@@ -226,7 +229,11 @@ function mongo_collection:insert(doc)
|
||||
sock:request(pack)
|
||||
end
|
||||
|
||||
function mongo_collection:batch_insert(docs)
|
||||
function mongo_collection:safe_insert(doc)
|
||||
return self.database:runCommand("insert", self.name, "documents", {bson_encode(doc)})
|
||||
end
|
||||
|
||||
function mongo_collection:batch_insert(docs)
|
||||
for i=1,#docs do
|
||||
if docs[i]._id == nil then
|
||||
docs[i]._id = bson.objectid()
|
||||
@@ -275,6 +282,62 @@ function mongo_collection:find(query, selector)
|
||||
} , cursor_meta)
|
||||
end
|
||||
|
||||
-- collection:createIndex({username = 1}, {unique = true})
|
||||
function mongo_collection:createIndex(keys, option)
|
||||
local name = option.name
|
||||
option.name = nil
|
||||
|
||||
if not name then
|
||||
for k, v in pairs(keys) do
|
||||
name = (name == nil) and k or (name .. "_" .. k)
|
||||
name = name .. "_" .. v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local doc = {};
|
||||
doc.name = name
|
||||
doc.key = keys
|
||||
for k, v in pairs(option) do
|
||||
doc[k] = v
|
||||
end
|
||||
return self.database:runCommand("createIndexes", self.name, "indexes", {doc})
|
||||
end
|
||||
|
||||
mongo_collection.ensureIndex = mongo_collection.createIndex;
|
||||
|
||||
|
||||
function mongo_collection:drop()
|
||||
return self.database:runCommand("drop", self.name)
|
||||
end
|
||||
|
||||
-- collection:dropIndex("age_1")
|
||||
-- collection:dropIndex("*")
|
||||
function mongo_collection:dropIndex(indexName)
|
||||
return self.database:runCommand("dropIndexes", self.name, "index", indexName)
|
||||
end
|
||||
|
||||
-- collection:findAndModify({query = {name = "userid"}, update = {["$inc"] = {nextid = 1}}, })
|
||||
-- keys, value type
|
||||
-- query, table
|
||||
-- sort, table
|
||||
-- remove, bool
|
||||
-- update, table
|
||||
-- new, bool
|
||||
-- fields, bool
|
||||
-- upsert, boolean
|
||||
function mongo_collection:findAndModify(doc)
|
||||
assert(doc.query)
|
||||
assert(doc.update or doc.remove)
|
||||
|
||||
local cmd = {"findAndModify", self.name};
|
||||
for k, v in pairs(doc) do
|
||||
table.insert(cmd, k)
|
||||
table.insert(cmd, v)
|
||||
end
|
||||
return self.database:runCommand(unpack(cmd))
|
||||
end
|
||||
|
||||
function mongo_cursor:hasNext()
|
||||
if self.__ptr == nil then
|
||||
if self.__document == nil then
|
||||
|
||||
747
lualib/mysql.lua
Executable file
747
lualib/mysql.lua
Executable file
@@ -0,0 +1,747 @@
|
||||
-- Copyright (C) 2012 Yichun Zhang (agentzh)
|
||||
-- Copyright (C) 2014 Chang Feng
|
||||
-- This file is modified version from https://github.com/openresty/lua-resty-mysql
|
||||
-- The license is under the BSD license.
|
||||
|
||||
local socketchannel = require "socketchannel"
|
||||
local bit = require "bit32"
|
||||
local mysqlaux = require "mysqlaux.c"
|
||||
local crypt = require "crypt"
|
||||
|
||||
|
||||
local sub = string.sub
|
||||
local strbyte = string.byte
|
||||
local strchar = string.char
|
||||
local strfind = string.find
|
||||
local strrep = string.rep
|
||||
local null = nil
|
||||
local band = bit.band
|
||||
local bxor = bit.bxor
|
||||
local bor = bit.bor
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
local sha1= crypt.sha1
|
||||
local concat = table.concat
|
||||
local unpack = unpack
|
||||
local setmetatable = setmetatable
|
||||
local error = error
|
||||
local tonumber = tonumber
|
||||
local new_tab = function (narr, nrec) return {} end
|
||||
|
||||
|
||||
local _M = { _VERSION = '0.13' }
|
||||
-- constants
|
||||
|
||||
local STATE_CONNECTED = 1
|
||||
local STATE_COMMAND_SENT = 2
|
||||
|
||||
local COM_QUERY = 0x03
|
||||
|
||||
local SERVER_MORE_RESULTS_EXISTS = 8
|
||||
|
||||
-- 16MB - 1, the default max allowed packet size used by libmysqlclient
|
||||
local FULL_PACKET_SIZE = 16777215
|
||||
|
||||
|
||||
local mt = { __index = _M }
|
||||
|
||||
|
||||
-- mysql field value type converters
|
||||
local converters = new_tab(0, 8)
|
||||
|
||||
for i = 0x01, 0x05 do
|
||||
-- tiny, short, long, float, double
|
||||
converters[i] = tonumber
|
||||
end
|
||||
-- converters[0x08] = tonumber -- long long
|
||||
converters[0x09] = tonumber -- int24
|
||||
converters[0x0d] = tonumber -- year
|
||||
converters[0xf6] = tonumber -- newdecimal
|
||||
|
||||
|
||||
local function _get_byte2(data, i)
|
||||
local a, b = strbyte(data, i, i + 1)
|
||||
return bor(a, lshift(b, 8)), i + 2
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte3(data, i)
|
||||
local a, b, c = strbyte(data, i, i + 2)
|
||||
return bor(a, lshift(b, 8), lshift(c, 16)), i + 3
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte4(data, i)
|
||||
local a, b, c, d = strbyte(data, i, i + 3)
|
||||
return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24)), i + 4
|
||||
end
|
||||
|
||||
|
||||
local function _get_byte8(data, i)
|
||||
local a, b, c, d, e, f, g, h = strbyte(data, i, i + 7)
|
||||
|
||||
-- XXX workaround for the lack of 64-bit support in bitop:
|
||||
local lo = bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24))
|
||||
local hi = bor(e, lshift(f, 8), lshift(g, 16), lshift(h, 24))
|
||||
return lo + hi * 4294967296, i + 8
|
||||
|
||||
-- return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24), lshift(e, 32),
|
||||
-- lshift(f, 40), lshift(g, 48), lshift(h, 56)), i + 8
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte2(n)
|
||||
return strchar(band(n, 0xff), band(rshift(n, 8), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte3(n)
|
||||
return strchar(band(n, 0xff),
|
||||
band(rshift(n, 8), 0xff),
|
||||
band(rshift(n, 16), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _set_byte4(n)
|
||||
return strchar(band(n, 0xff),
|
||||
band(rshift(n, 8), 0xff),
|
||||
band(rshift(n, 16), 0xff),
|
||||
band(rshift(n, 24), 0xff))
|
||||
end
|
||||
|
||||
|
||||
local function _from_cstring(data, i)
|
||||
local last = strfind(data, "\0", i, true)
|
||||
if not last then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return sub(data, i, last), last + 1
|
||||
end
|
||||
|
||||
|
||||
local function _to_cstring(data)
|
||||
return data .. "\0"
|
||||
end
|
||||
|
||||
|
||||
local function _to_binary_coded_string(data)
|
||||
return strchar(#data) .. data
|
||||
end
|
||||
|
||||
|
||||
local function _dump(data)
|
||||
local len = #data
|
||||
local bytes = new_tab(len, 0)
|
||||
for i = 1, len do
|
||||
bytes[i] = strbyte(data, i)
|
||||
end
|
||||
return concat(bytes, " ")
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function _dumphex(bytes)
|
||||
local result ={}
|
||||
|
||||
for i = 1, string.len(bytes) do
|
||||
local charcode = tonumber(strbyte(bytes, i, i))
|
||||
local hexstr = string.format("%02X", charcode)
|
||||
result[i]=hexstr
|
||||
end
|
||||
|
||||
local res=table.concat(result, " ")
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function _compute_token(password, scramble)
|
||||
if password == "" then
|
||||
return ""
|
||||
end
|
||||
--_dump(scramble)
|
||||
|
||||
local stage1 = sha1(password)
|
||||
--print("stage1:", _dumphex(stage1) )
|
||||
local stage2 = sha1(stage1)
|
||||
local stage3 = sha1(scramble .. stage2)
|
||||
local n = #stage1
|
||||
local bytes = new_tab(n, 0)
|
||||
for i = 1, n do
|
||||
bytes[i] = strchar(bxor(strbyte(stage3, i), strbyte(stage1, i)))
|
||||
end
|
||||
|
||||
return concat(bytes)
|
||||
end
|
||||
|
||||
local function _compose_packet(self, req, size)
|
||||
self.packet_no = self.packet_no + 1
|
||||
|
||||
local packet = _set_byte3(size) .. strchar(self.packet_no) .. req
|
||||
return packet
|
||||
end
|
||||
|
||||
|
||||
local function _send_packet(self, req, size)
|
||||
local sock = self.sock
|
||||
|
||||
self.packet_no = self.packet_no + 1
|
||||
|
||||
|
||||
local packet = _set_byte3(size) .. strchar(self.packet_no) .. req
|
||||
|
||||
return socket.write(self.sock,packet)
|
||||
end
|
||||
|
||||
|
||||
local function _recv_packet(self,sock)
|
||||
|
||||
|
||||
local data = sock:read( 4)
|
||||
if not data then
|
||||
return nil, nil, "failed to receive packet header: "
|
||||
end
|
||||
|
||||
|
||||
local len, pos = _get_byte3(data, 1)
|
||||
|
||||
|
||||
if len == 0 then
|
||||
return nil, nil, "empty packet"
|
||||
end
|
||||
|
||||
if len > self._max_packet_size then
|
||||
return nil, nil, "packet size too big: " .. len
|
||||
end
|
||||
|
||||
local num = strbyte(data, pos)
|
||||
|
||||
self.packet_no = num
|
||||
|
||||
data = sock:read(len)
|
||||
|
||||
if not data then
|
||||
return nil, nil, "failed to read packet content: "
|
||||
end
|
||||
|
||||
|
||||
local field_count = strbyte(data, 1)
|
||||
local typ
|
||||
if field_count == 0x00 then
|
||||
typ = "OK"
|
||||
elseif field_count == 0xff then
|
||||
typ = "ERR"
|
||||
elseif field_count == 0xfe then
|
||||
typ = "EOF"
|
||||
elseif field_count <= 250 then
|
||||
typ = "DATA"
|
||||
end
|
||||
|
||||
return data, typ
|
||||
end
|
||||
|
||||
|
||||
local function _from_length_coded_bin(data, pos)
|
||||
local first = strbyte(data, pos)
|
||||
|
||||
if not first then
|
||||
return nil, pos
|
||||
end
|
||||
|
||||
if first >= 0 and first <= 250 then
|
||||
return first, pos + 1
|
||||
end
|
||||
|
||||
if first == 251 then
|
||||
return null, pos + 1
|
||||
end
|
||||
|
||||
if first == 252 then
|
||||
pos = pos + 1
|
||||
return _get_byte2(data, pos)
|
||||
end
|
||||
|
||||
if first == 253 then
|
||||
pos = pos + 1
|
||||
return _get_byte3(data, pos)
|
||||
end
|
||||
|
||||
if first == 254 then
|
||||
pos = pos + 1
|
||||
return _get_byte8(data, pos)
|
||||
end
|
||||
|
||||
return false, pos + 1
|
||||
end
|
||||
|
||||
|
||||
local function _from_length_coded_str(data, pos)
|
||||
local len
|
||||
len, pos = _from_length_coded_bin(data, pos)
|
||||
if len == nil or len == null then
|
||||
return null, pos
|
||||
end
|
||||
|
||||
return sub(data, pos, pos + len - 1), pos + len
|
||||
end
|
||||
|
||||
|
||||
local function _parse_ok_packet(packet)
|
||||
local res = new_tab(0, 5)
|
||||
local pos
|
||||
|
||||
res.affected_rows, pos = _from_length_coded_bin(packet, 2)
|
||||
|
||||
res.insert_id, pos = _from_length_coded_bin(packet, pos)
|
||||
|
||||
res.server_status, pos = _get_byte2(packet, pos)
|
||||
|
||||
res.warning_count, pos = _get_byte2(packet, pos)
|
||||
|
||||
|
||||
local message = sub(packet, pos)
|
||||
if message and message ~= "" then
|
||||
res.message = message
|
||||
end
|
||||
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function _parse_eof_packet(packet)
|
||||
local pos = 2
|
||||
|
||||
local warning_count, pos = _get_byte2(packet, pos)
|
||||
local status_flags = _get_byte2(packet, pos)
|
||||
|
||||
return warning_count, status_flags
|
||||
end
|
||||
|
||||
|
||||
local function _parse_err_packet(packet)
|
||||
local errno, pos = _get_byte2(packet, 2)
|
||||
local marker = sub(packet, pos, pos)
|
||||
local sqlstate
|
||||
if marker == '#' then
|
||||
-- with sqlstate
|
||||
pos = pos + 1
|
||||
sqlstate = sub(packet, pos, pos + 5 - 1)
|
||||
pos = pos + 5
|
||||
end
|
||||
|
||||
local message = sub(packet, pos)
|
||||
return errno, message, sqlstate
|
||||
end
|
||||
|
||||
|
||||
local function _parse_result_set_header_packet(packet)
|
||||
local field_count, pos = _from_length_coded_bin(packet, 1)
|
||||
|
||||
local extra
|
||||
extra = _from_length_coded_bin(packet, pos)
|
||||
|
||||
return field_count, extra
|
||||
end
|
||||
|
||||
|
||||
local function _parse_field_packet(data)
|
||||
local col = new_tab(0, 2)
|
||||
local catalog, db, table, orig_table, orig_name, charsetnr, length
|
||||
local pos
|
||||
catalog, pos = _from_length_coded_str(data, 1)
|
||||
|
||||
|
||||
db, pos = _from_length_coded_str(data, pos)
|
||||
table, pos = _from_length_coded_str(data, pos)
|
||||
orig_table, pos = _from_length_coded_str(data, pos)
|
||||
col.name, pos = _from_length_coded_str(data, pos)
|
||||
|
||||
orig_name, pos = _from_length_coded_str(data, pos)
|
||||
|
||||
pos = pos + 1 -- ignore the filler
|
||||
|
||||
charsetnr, pos = _get_byte2(data, pos)
|
||||
|
||||
length, pos = _get_byte4(data, pos)
|
||||
|
||||
col.type = strbyte(data, pos)
|
||||
|
||||
--[[
|
||||
pos = pos + 1
|
||||
|
||||
col.flags, pos = _get_byte2(data, pos)
|
||||
|
||||
col.decimals = strbyte(data, pos)
|
||||
pos = pos + 1
|
||||
|
||||
local default = sub(data, pos + 2)
|
||||
if default and default ~= "" then
|
||||
col.default = default
|
||||
end
|
||||
--]]
|
||||
|
||||
return col
|
||||
end
|
||||
|
||||
|
||||
local function _parse_row_data_packet(data, cols, compact)
|
||||
local pos = 1
|
||||
local ncols = #cols
|
||||
local row
|
||||
if compact then
|
||||
row = new_tab(ncols, 0)
|
||||
else
|
||||
row = new_tab(0, ncols)
|
||||
end
|
||||
for i = 1, ncols do
|
||||
local value
|
||||
value, pos = _from_length_coded_str(data, pos)
|
||||
local col = cols[i]
|
||||
local typ = col.type
|
||||
local name = col.name
|
||||
|
||||
if value ~= null then
|
||||
local conv = converters[typ]
|
||||
if conv then
|
||||
value = conv(value)
|
||||
end
|
||||
end
|
||||
|
||||
if compact then
|
||||
row[i] = value
|
||||
|
||||
else
|
||||
row[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
|
||||
local function _recv_field_packet(self, sock)
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
return nil, msg, errno, sqlstate
|
||||
end
|
||||
|
||||
if typ ~= 'DATA' then
|
||||
return nil, "bad field packet type: " .. typ
|
||||
end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
return _parse_field_packet(packet)
|
||||
end
|
||||
|
||||
local function _recv_decode_packet_resp(self)
|
||||
return function(sock)
|
||||
return true, _recv_packet(self,sock)
|
||||
end
|
||||
end
|
||||
|
||||
local function _recv_auth_resp(self)
|
||||
return function(sock)
|
||||
local packet, typ, err = _recv_packet(self,sock)
|
||||
if not packet then
|
||||
--print("recv auth resp : failed to receive the result packet")
|
||||
error ("failed to receive the result packet"..err)
|
||||
--return nil,err
|
||||
end
|
||||
|
||||
if typ == 'ERR' then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
--return nil, errno,msg, sqlstate
|
||||
end
|
||||
|
||||
if typ == 'EOF' then
|
||||
error "old pre-4.1 authentication protocol not supported"
|
||||
end
|
||||
|
||||
if typ ~= 'OK' then
|
||||
error "bad packet type: "
|
||||
end
|
||||
return true, true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function _mysql_login(self,user,password,database)
|
||||
|
||||
return function(sockchannel)
|
||||
local packet, typ, err = sockchannel:response( _recv_decode_packet_resp(self) )
|
||||
--local aat={}
|
||||
if not packet then
|
||||
error( err )
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
self.protocol_ver = strbyte(packet)
|
||||
|
||||
local server_ver, pos = _from_cstring(packet, 2)
|
||||
if not server_ver then
|
||||
error "bad handshake initialization packet: bad server version"
|
||||
end
|
||||
|
||||
self._server_ver = server_ver
|
||||
|
||||
|
||||
local thread_id, pos = _get_byte4(packet, pos)
|
||||
|
||||
local scramble1 = sub(packet, pos, pos + 8 - 1)
|
||||
if not scramble1 then
|
||||
error "1st part of scramble not found"
|
||||
end
|
||||
|
||||
pos = pos + 9 -- skip filler
|
||||
|
||||
-- two lower bytes
|
||||
self._server_capabilities, pos = _get_byte2(packet, pos)
|
||||
|
||||
self._server_lang = strbyte(packet, pos)
|
||||
pos = pos + 1
|
||||
|
||||
self._server_status, pos = _get_byte2(packet, pos)
|
||||
|
||||
local more_capabilities
|
||||
more_capabilities, pos = _get_byte2(packet, pos)
|
||||
|
||||
self._server_capabilities = bor(self._server_capabilities,
|
||||
lshift(more_capabilities, 16))
|
||||
|
||||
|
||||
local len = 21 - 8 - 1
|
||||
|
||||
pos = pos + 1 + 10
|
||||
|
||||
local scramble_part2 = sub(packet, pos, pos + len - 1)
|
||||
if not scramble_part2 then
|
||||
error "2nd part of scramble not found"
|
||||
end
|
||||
|
||||
|
||||
local scramble = scramble1..scramble_part2
|
||||
local token = _compute_token(password, scramble)
|
||||
|
||||
local client_flags = 260047;
|
||||
|
||||
local req = _set_byte4(client_flags)
|
||||
.. _set_byte4(self._max_packet_size)
|
||||
.. "\0" -- TODO: add support for charset encoding
|
||||
.. strrep("\0", 23)
|
||||
.. _to_cstring(user)
|
||||
.. _to_binary_coded_string(token)
|
||||
.. _to_cstring(database)
|
||||
|
||||
local packet_len = 4 + 4 + 1 + 23 + #user + 1
|
||||
+ #token + 1 + #database + 1
|
||||
|
||||
local authpacket=_compose_packet(self,req,packet_len)
|
||||
return sockchannel:request(authpacket,_recv_auth_resp(self))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function _compose_query(self, query)
|
||||
|
||||
self.packet_no = -1
|
||||
|
||||
local cmd_packet = strchar(COM_QUERY) .. query
|
||||
local packet_len = 1 + #query
|
||||
|
||||
local querypacket = _compose_packet(self, cmd_packet, packet_len)
|
||||
return querypacket
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function read_result(self, sock)
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
return nil, err
|
||||
--error( err )
|
||||
end
|
||||
|
||||
if typ == "ERR" then
|
||||
local errno, msg, sqlstate = _parse_err_packet(packet)
|
||||
return nil, msg, errno, sqlstate
|
||||
--error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
if typ == 'OK' then
|
||||
local res = _parse_ok_packet(packet)
|
||||
if res and band(res.server_status, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
|
||||
return res, "again"
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
if typ ~= 'DATA' then
|
||||
return nil, "packet type " .. typ .. " not supported"
|
||||
--error( "packet type " .. typ .. " not supported" )
|
||||
end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
local field_count, extra = _parse_result_set_header_packet(packet)
|
||||
|
||||
local cols = new_tab(field_count, 0)
|
||||
for i = 1, field_count do
|
||||
local col, err, errno, sqlstate = _recv_field_packet(self, sock)
|
||||
if not col then
|
||||
return nil, err, errno, sqlstate
|
||||
--error( string.format("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate))
|
||||
end
|
||||
|
||||
cols[i] = col
|
||||
end
|
||||
|
||||
local packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
--error( err)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ ~= 'EOF' then
|
||||
--error ( "unexpected packet type " .. typ .. " while eof packet is ".. "expected" )
|
||||
return nil, "unexpected packet type " .. typ .. " while eof packet is ".. "expected"
|
||||
end
|
||||
|
||||
-- typ == 'EOF'
|
||||
|
||||
local compact = self.compact
|
||||
|
||||
local rows = new_tab( 4, 0)
|
||||
local i = 0
|
||||
while true do
|
||||
packet, typ, err = _recv_packet(self, sock)
|
||||
if not packet then
|
||||
--error (err)
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if typ == 'EOF' then
|
||||
local warning_count, status_flags = _parse_eof_packet(packet)
|
||||
|
||||
if band(status_flags, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
|
||||
return rows, "again"
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
-- if typ ~= 'DATA' then
|
||||
-- return nil, 'bad row packet type: ' .. typ
|
||||
-- end
|
||||
|
||||
-- typ == 'DATA'
|
||||
|
||||
local row = _parse_row_data_packet(packet, cols, compact)
|
||||
i = i + 1
|
||||
rows[i] = row
|
||||
end
|
||||
|
||||
return rows
|
||||
end
|
||||
|
||||
local function _query_resp(self)
|
||||
return function(sock)
|
||||
local res, err, errno, sqlstate = read_result(self,sock)
|
||||
if not res then
|
||||
local badresult ={}
|
||||
badresult.badresult = true
|
||||
badresult.err = err
|
||||
badresult.errno = errno
|
||||
badresult.sqlstate = sqlstate
|
||||
return true , badresult
|
||||
end
|
||||
if err ~= "again" then
|
||||
return true, res
|
||||
end
|
||||
local mulitresultset = {res}
|
||||
mulitresultset.mulitresultset = true
|
||||
local i =2
|
||||
while err =="again" do
|
||||
res, err, errno, sqlstate = read_result(self,sock)
|
||||
if not res then
|
||||
return true, mulitresultset
|
||||
end
|
||||
mulitresultset[i]=res
|
||||
i=i+1
|
||||
end
|
||||
return true, mulitresultset
|
||||
end
|
||||
end
|
||||
|
||||
function _M.connect( opts)
|
||||
|
||||
local self = setmetatable( {}, mt)
|
||||
|
||||
local max_packet_size = opts.max_packet_size
|
||||
if not max_packet_size then
|
||||
max_packet_size = 1024 * 1024 -- default 1 MB
|
||||
end
|
||||
self._max_packet_size = max_packet_size
|
||||
self.compact = opts.compact_arrays
|
||||
|
||||
|
||||
local database = opts.database or ""
|
||||
local user = opts.user or ""
|
||||
local password = opts.password or ""
|
||||
|
||||
local channel = socketchannel.channel {
|
||||
host = opts.host,
|
||||
port = opts.port or 3306,
|
||||
auth = _mysql_login(self,user,password,database ),
|
||||
}
|
||||
-- try connect first only once
|
||||
channel:connect(true)
|
||||
self.sockchannel = channel
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _M.disconnect(self)
|
||||
self.sockchannel:close()
|
||||
setmetatable(self, nil)
|
||||
end
|
||||
|
||||
|
||||
function _M.query(self, query)
|
||||
local querypacket = _compose_query(self, query)
|
||||
local sockchannel = self.sockchannel
|
||||
if not self.query_resp then
|
||||
self.query_resp = _query_resp(self)
|
||||
end
|
||||
return sockchannel:request( querypacket, self.query_resp )
|
||||
end
|
||||
|
||||
function _M.server_ver(self)
|
||||
return self._server_ver
|
||||
end
|
||||
|
||||
|
||||
function _M.quote_sql_str( str)
|
||||
return mysqlaux.quote_sql_str(str)
|
||||
end
|
||||
|
||||
function _M.set_compact_arrays(self, value)
|
||||
self.compact = value
|
||||
end
|
||||
|
||||
|
||||
return _M
|
||||
@@ -84,6 +84,7 @@ function redis.connect(db_conf)
|
||||
host = db_conf.host,
|
||||
port = db_conf.port or 6379,
|
||||
auth = redis_login(db_conf.auth, db_conf.db),
|
||||
nodelay = true,
|
||||
}
|
||||
-- try connect first only once
|
||||
channel:connect(true)
|
||||
@@ -199,6 +200,7 @@ function redis.watch(db_conf)
|
||||
host = db_conf.host,
|
||||
port = db_conf.port or 6379,
|
||||
auth = watch_login(obj, db_conf.auth),
|
||||
nodelay = true,
|
||||
}
|
||||
obj.__sock = channel
|
||||
|
||||
|
||||
@@ -53,21 +53,26 @@ local function genkey(self)
|
||||
return key
|
||||
end
|
||||
|
||||
function meta:__index(key)
|
||||
local function getcobj(self)
|
||||
local obj = self.__obj
|
||||
if isdirty(obj) then
|
||||
local newobj, newtbl = needupdate(self.__gcobj)
|
||||
if newobj then
|
||||
local newgcobj = newtbl.__gcobj
|
||||
-- todo: update
|
||||
local root = findroot(self)
|
||||
update(root, newobj, newgcobj)
|
||||
if obj == self.__obj then
|
||||
error ("The key [" .. genkey(self) .. "] doesn't exist after update")
|
||||
end
|
||||
obj = self.__obj
|
||||
end
|
||||
end
|
||||
local v = index(self.__obj, key)
|
||||
return obj
|
||||
end
|
||||
|
||||
function meta:__index(key)
|
||||
local obj = getcobj(self)
|
||||
local v = index(obj, key)
|
||||
if type(v) == "userdata" then
|
||||
local r = setmetatable({
|
||||
__obj = v,
|
||||
@@ -83,11 +88,11 @@ function meta:__index(key)
|
||||
end
|
||||
|
||||
function meta:__len()
|
||||
return len(self.__obj)
|
||||
return len(getcobj(self))
|
||||
end
|
||||
|
||||
local function conf_ipairs(self, index)
|
||||
local obj = self.__obj
|
||||
local obj = getcobj(self)
|
||||
index = index + 1
|
||||
local value = rawget(self, index)
|
||||
if value then
|
||||
@@ -109,7 +114,8 @@ function meta:__pairs()
|
||||
end
|
||||
|
||||
function conf.next(obj, key)
|
||||
local nextkey = core.nextkey(obj.__obj, key)
|
||||
local cobj = getcobj(obj)
|
||||
local nextkey = core.nextkey(cobj, key)
|
||||
if nextkey then
|
||||
return nextkey, obj[nextkey]
|
||||
end
|
||||
@@ -127,7 +133,7 @@ end
|
||||
|
||||
function conf.update(self, pointer)
|
||||
local cobj = self.__obj
|
||||
assert(isdirty(cobj), "Obly dirty object can be update")
|
||||
assert(isdirty(cobj), "Only dirty object can be update")
|
||||
core.update(self.__gcobj, pointer, { __gcobj = core.box(pointer) })
|
||||
end
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ end
|
||||
|
||||
local coroutine_pool = {}
|
||||
local coroutine_yield = coroutine.yield
|
||||
local coroutine_count = 0
|
||||
|
||||
local function co_create(f)
|
||||
local co = table.remove(coroutine_pool)
|
||||
@@ -109,6 +110,11 @@ local function co_create(f)
|
||||
f(coroutine_yield())
|
||||
end
|
||||
end)
|
||||
coroutine_count = coroutine_count + 1
|
||||
if coroutine_count > 1024 then
|
||||
skynet.error("May overload, create 1024 task")
|
||||
coroutine_count = 0
|
||||
end
|
||||
else
|
||||
coroutine.resume(co, f)
|
||||
end
|
||||
@@ -122,7 +128,7 @@ local function dispatch_wakeup()
|
||||
local session = sleep_session[co]
|
||||
if session then
|
||||
session_id_coroutine[session] = "BREAK"
|
||||
return suspend(co, coroutine.resume(co, true))
|
||||
return suspend(co, coroutine.resume(co, true, "BREAK"))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -143,13 +149,15 @@ end
|
||||
function suspend(co, result, command, param, size)
|
||||
if not result then
|
||||
local session = session_coroutine_id[co]
|
||||
local addr = session_coroutine_address[co]
|
||||
if session ~= 0 then
|
||||
-- only call response error
|
||||
c.send(addr, skynet.PTYPE_ERROR, session, "")
|
||||
if session then -- coroutine may fork by others (session is nil)
|
||||
local addr = session_coroutine_address[co]
|
||||
if session ~= 0 then
|
||||
-- only call response error
|
||||
c.send(addr, skynet.PTYPE_ERROR, session, "")
|
||||
end
|
||||
session_coroutine_id[co] = nil
|
||||
session_coroutine_address[co] = nil
|
||||
end
|
||||
session_coroutine_id[co] = nil
|
||||
session_coroutine_address[co] = nil
|
||||
error(debug.traceback(co,tostring(command)))
|
||||
end
|
||||
if command == "CALL" then
|
||||
@@ -166,7 +174,7 @@ function suspend(co, result, command, param, size)
|
||||
session_response[co] = true
|
||||
local ret
|
||||
if not dead_service[co_address] then
|
||||
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) >= 0
|
||||
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) ~= nil
|
||||
elseif size == nil then
|
||||
c.trash(param, size)
|
||||
ret = false
|
||||
@@ -200,9 +208,9 @@ function suspend(co, result, command, param, size)
|
||||
local ret
|
||||
if not dead_service[co_address] then
|
||||
if ok then
|
||||
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, f(...)) >=0
|
||||
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, f(...)) ~= nil
|
||||
else
|
||||
ret = c.send(co_address, skynet.PTYPE_ERROR, co_session, "") >=0
|
||||
ret = c.send(co_address, skynet.PTYPE_ERROR, co_session, "") ~= nil
|
||||
end
|
||||
else
|
||||
ret = false
|
||||
@@ -246,7 +254,8 @@ function skynet.sleep(ti)
|
||||
session = tonumber(session)
|
||||
local succ, ret = coroutine_yield("SLEEP", session)
|
||||
sleep_session[coroutine.running()] = nil
|
||||
if ret == true then
|
||||
assert(succ, ret)
|
||||
if ret == "BREAK" then
|
||||
return "BREAK"
|
||||
end
|
||||
end
|
||||
@@ -328,6 +337,7 @@ function skynet.time()
|
||||
end
|
||||
|
||||
function skynet.exit()
|
||||
fork_queue = {} -- no fork coroutine can be execute after skynet.exit
|
||||
skynet.send(".launcher","lua","REMOVE",skynet.self())
|
||||
-- report the sources that call me
|
||||
for co, session in pairs(session_coroutine_id) do
|
||||
@@ -695,8 +705,19 @@ function skynet.task(ret)
|
||||
return t
|
||||
end
|
||||
|
||||
function skynet.term(service)
|
||||
return _error_dispatch(0, service)
|
||||
end
|
||||
|
||||
local function clear_pool()
|
||||
coroutine_pool = {}
|
||||
end
|
||||
|
||||
-- Inject internal debug framework
|
||||
local debug = require "skynet.debug"
|
||||
debug(skynet, dispatch_message)
|
||||
debug(skynet, {
|
||||
dispatch = dispatch_message,
|
||||
clear = clear_pool,
|
||||
})
|
||||
|
||||
return skynet
|
||||
|
||||
@@ -2,7 +2,7 @@ local io = io
|
||||
local table = table
|
||||
local debug = debug
|
||||
|
||||
return function (skynet, dispatch_func)
|
||||
return function (skynet, export)
|
||||
|
||||
local internal_info_func
|
||||
|
||||
@@ -18,7 +18,7 @@ function dbgcmd.MEM()
|
||||
end
|
||||
|
||||
function dbgcmd.GC()
|
||||
coroutine_pool = {}
|
||||
export.clear()
|
||||
collectgarbage "collect"
|
||||
end
|
||||
|
||||
@@ -49,11 +49,15 @@ end
|
||||
|
||||
function dbgcmd.RUN(source, filename)
|
||||
local inject = require "skynet.inject"
|
||||
local output = inject(source, filename , dispatch_func, skynet.register_protocol)
|
||||
local output = inject(source, filename , export.dispatch, skynet.register_protocol)
|
||||
collectgarbage "collect"
|
||||
skynet.ret(skynet.pack(table.concat(output, "\n")))
|
||||
end
|
||||
|
||||
function dbgcmd.TERM(service)
|
||||
skynet.term(service)
|
||||
end
|
||||
|
||||
local function _debug_dispatch(session, address, cmd, ...)
|
||||
local f = dbgcmd[cmd]
|
||||
assert(f, cmd)
|
||||
|
||||
@@ -19,4 +19,8 @@ function harbor.connect(id)
|
||||
skynet.call(".cslave", "lua", "CONNECT", id)
|
||||
end
|
||||
|
||||
function harbor.linkmaster()
|
||||
skynet.call(".cslave", "lua", "LINKMASTER")
|
||||
end
|
||||
|
||||
return harbor
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
local skynet = require "skynet"
|
||||
local coroutine = coroutine
|
||||
local pcall = pcall
|
||||
local xpcall = xpcall
|
||||
local traceback = debug.traceback
|
||||
local table = table
|
||||
|
||||
function skynet.queue()
|
||||
@@ -9,25 +10,24 @@ function skynet.queue()
|
||||
local thread_queue = {}
|
||||
return function(f, ...)
|
||||
local thread = coroutine.running()
|
||||
if ref == 0 then
|
||||
current_thread = thread
|
||||
elseif current_thread ~= thread then
|
||||
if current_thread and current_thread ~= thread then
|
||||
table.insert(thread_queue, thread)
|
||||
skynet.wait()
|
||||
assert(ref == 0)
|
||||
assert(ref == 0) -- current_thread == thread
|
||||
end
|
||||
current_thread = thread
|
||||
|
||||
ref = ref + 1
|
||||
local ok, err = pcall(f, ...)
|
||||
local ok, err = xpcall(f, traceback, ...)
|
||||
ref = ref - 1
|
||||
if ref == 0 then
|
||||
current_thread = nil
|
||||
local co = table.remove(thread_queue,1)
|
||||
if co then
|
||||
skynet.wakeup(co)
|
||||
current_thread = table.remove(thread_queue,1)
|
||||
if current_thread then
|
||||
skynet.wakeup(current_thread)
|
||||
end
|
||||
end
|
||||
assert(ok,err)
|
||||
end
|
||||
end
|
||||
|
||||
return skynet.queue
|
||||
return skynet.queue
|
||||
|
||||
@@ -132,6 +132,14 @@ function snax.kill(obj, ...)
|
||||
skynet_call(obj.handle, "snax", t.system.exit, ...)
|
||||
end
|
||||
|
||||
function snax.self()
|
||||
return snax.bind(skynet.self(), SERVICE_NAME)
|
||||
end
|
||||
|
||||
function snax.exit(...)
|
||||
snax.kill(snax.self(), ...)
|
||||
end
|
||||
|
||||
local function test_result(ok, ...)
|
||||
if ok then
|
||||
return ...
|
||||
|
||||
@@ -37,6 +37,7 @@ function gateserver.start(handler)
|
||||
local port = assert(conf.port)
|
||||
maxclient = conf.maxclient or 1024
|
||||
nodelay = conf.nodelay
|
||||
skynet.error(string.format("Listen on %s:%d", address, port))
|
||||
socket = socketdriver.listen(address, port)
|
||||
socketdriver.start(socket)
|
||||
if handler.open then
|
||||
@@ -52,20 +53,32 @@ function gateserver.start(handler)
|
||||
|
||||
local MSG = {}
|
||||
|
||||
function MSG.data(fd, msg, sz)
|
||||
local function dispatch_msg(fd, msg, sz)
|
||||
if connection[fd] then
|
||||
handler.message(fd, msg, sz)
|
||||
else
|
||||
skynet.error(string.format("Drop message from fd (%d) : %s", fd, netpack.tostring(msg,sz)))
|
||||
end
|
||||
end
|
||||
|
||||
function MSG.more()
|
||||
for fd, msg, sz in netpack.pop, queue do
|
||||
if connection[fd] then
|
||||
handler.message(fd, msg, sz)
|
||||
MSG.data = dispatch_msg
|
||||
|
||||
local function dispatch_queue()
|
||||
local fd, msg, sz = netpack.pop(queue)
|
||||
if fd then
|
||||
-- may dispatch even the handler.message blocked
|
||||
-- If the handler.message never block, the queue should be empty, so only fork once and then exit.
|
||||
skynet.fork(dispatch_queue)
|
||||
dispatch_msg(fd, msg, sz)
|
||||
|
||||
for fd, msg, sz in netpack.pop, queue do
|
||||
dispatch_msg(fd, msg, sz)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MSG.more = dispatch_queue
|
||||
|
||||
function MSG.open(fd, msg)
|
||||
if client_number >= maxclient then
|
||||
socketdriver.close(fd)
|
||||
@@ -96,7 +109,7 @@ function gateserver.start(handler)
|
||||
|
||||
function MSG.error(fd, msg)
|
||||
if handler.error then
|
||||
handler.error(fd)
|
||||
handler.error(fd, msg)
|
||||
end
|
||||
close_fd(fd)
|
||||
end
|
||||
@@ -127,4 +140,4 @@ function gateserver.start(handler)
|
||||
end)
|
||||
end
|
||||
|
||||
return gateserver
|
||||
return gateserver
|
||||
|
||||
@@ -90,7 +90,7 @@ local function launch_slave(auth_handler)
|
||||
local function ret_pack(ok, err, ...)
|
||||
if ok then
|
||||
skynet.ret(skynet.pack(err, ...))
|
||||
elseif err ~= socket_error then
|
||||
else
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
@@ -163,6 +163,7 @@ local function launch_master(conf)
|
||||
if err ~= socket_error then
|
||||
skynet.error(string.format("invalid client (fd = %d) error = %s", fd, err))
|
||||
end
|
||||
socket.start(fd)
|
||||
end
|
||||
socket.close(fd)
|
||||
end)
|
||||
|
||||
@@ -58,7 +58,7 @@ socket_message[1] = function(id, size, data)
|
||||
end
|
||||
else
|
||||
if s.buffer_limit and sz > s.buffer_limit then
|
||||
skynet.error(string.format("socket buffer overlow: fd=%d size=%d", id , sz))
|
||||
skynet.error(string.format("socket buffer overflow: fd=%d size=%d", id , sz))
|
||||
driver.clear(s.buffer,buffer_pool)
|
||||
driver.close(id)
|
||||
return
|
||||
@@ -119,12 +119,23 @@ socket_message[5] = function(id)
|
||||
wakeup(s)
|
||||
end
|
||||
|
||||
-- SKYNET_SOCKET_TYPE_UDP = 6
|
||||
socket_message[6] = function(id, size, data, address)
|
||||
local s = socket_pool[id]
|
||||
if s == nil or s.callback == nil then
|
||||
skynet.error("socket: drop udp package from " .. id)
|
||||
driver.drop(data, size)
|
||||
return
|
||||
end
|
||||
s.callback(data, size, address)
|
||||
end
|
||||
|
||||
skynet.register_protocol {
|
||||
name = "socket",
|
||||
id = skynet.PTYPE_SOCKET, -- PTYPE_SOCKET = 6
|
||||
unpack = driver.unpack,
|
||||
dispatch = function (_, _, t, n1, n2, data)
|
||||
socket_message[t](n1,n2,data)
|
||||
dispatch = function (_, _, t, ...)
|
||||
socket_message[t](...)
|
||||
end
|
||||
}
|
||||
|
||||
@@ -137,9 +148,10 @@ local function connect(id, func)
|
||||
id = id,
|
||||
buffer = newbuffer,
|
||||
connected = false,
|
||||
read_require = false,
|
||||
read_required = false,
|
||||
co = false,
|
||||
callback = func,
|
||||
protocol = "TCP",
|
||||
}
|
||||
socket_pool[id] = s
|
||||
suspend(s)
|
||||
@@ -354,4 +366,39 @@ function socket.limit(id, limit)
|
||||
s.buffer_limit = limit
|
||||
end
|
||||
|
||||
---------------------- UDP
|
||||
|
||||
local udp_socket = {}
|
||||
|
||||
local function create_udp_object(id, cb)
|
||||
socket_pool[id] = {
|
||||
id = id,
|
||||
connected = true,
|
||||
protocol = "UDP",
|
||||
callback = cb,
|
||||
}
|
||||
end
|
||||
|
||||
function socket.udp(callback, host, port)
|
||||
local id = driver.udp(host, port)
|
||||
create_udp_object(id, callback)
|
||||
return id
|
||||
end
|
||||
|
||||
function socket.udp_connect(id, addr, port, callback)
|
||||
local obj = socket_pool[id]
|
||||
if obj then
|
||||
assert(obj.protocol == "UDP")
|
||||
if callback then
|
||||
obj.callback = callback
|
||||
end
|
||||
else
|
||||
create_udp_object(id, callback)
|
||||
end
|
||||
driver.udp_connect(id, addr, port)
|
||||
end
|
||||
|
||||
socket.sendto = assert(driver.udp_send)
|
||||
socket.udp_address = assert(driver.udp_address)
|
||||
|
||||
return socket
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local skynet = require "skynet"
|
||||
local socket = require "socket"
|
||||
local socketdriver = require "socketdriver"
|
||||
|
||||
-- channel support auto reconnect , and capture socket error in request/response transaction
|
||||
-- { host = "", port = , auth = function(so) , response = function(so) session, data }
|
||||
@@ -37,6 +38,7 @@ function socket_channel.channel(desc)
|
||||
__sock = false,
|
||||
__closed = false,
|
||||
__authcoroutine = false,
|
||||
__nodelay = desc.nodelay,
|
||||
}
|
||||
|
||||
return setmetatable(c, channel_meta)
|
||||
@@ -133,7 +135,7 @@ local function dispatch_by_order(self)
|
||||
else
|
||||
close_channel_socket(self)
|
||||
local errmsg
|
||||
if result ~= socket_error then
|
||||
if result_ok ~= socket_error then
|
||||
errmsg = result_ok
|
||||
end
|
||||
self.__result[co] = socket_error
|
||||
@@ -186,6 +188,9 @@ local function connect_once(self)
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.__nodelay then
|
||||
socketdriver.nodelay(fd)
|
||||
end
|
||||
|
||||
self.__sock = setmetatable( {fd} , channel_socket_meta )
|
||||
skynet.fork(dispatch_function(self), self)
|
||||
|
||||
@@ -313,9 +313,12 @@ local function packgroup(t,p)
|
||||
local tt, tp
|
||||
local alltypes = {}
|
||||
for name in pairs(t) do
|
||||
alltypes[name] = #alltypes
|
||||
table.insert(alltypes, name)
|
||||
end
|
||||
table.sort(alltypes) -- make result stable
|
||||
for idx, name in ipairs(alltypes) do
|
||||
alltypes[name] = idx - 1
|
||||
end
|
||||
tt = {}
|
||||
for _,name in ipairs(alltypes) do
|
||||
table.insert(tt, packtype(name, t[name], alltypes))
|
||||
|
||||
@@ -316,6 +316,7 @@ forward_local_messsage(struct harbor *h, void *msg, int sz) {
|
||||
destination = (destination & HANDLE_MASK) | ((uint32_t)h->id << HANDLE_REMOTE_SHIFT);
|
||||
|
||||
if (skynet_send(h->ctx, header.source, destination, type, (int)header.session, (void *)msg, sz-HEADER_COOKIE_LENGTH) < 0) {
|
||||
skynet_send(h->ctx, destination, header.source , PTYPE_ERROR, (int)header.session, NULL, 0);
|
||||
skynet_error(h->ctx, "Unknown destination :%x from :%x", destination, header.source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -9,6 +9,7 @@ local queryname = {}
|
||||
local harbor = {}
|
||||
local harbor_service
|
||||
local monitor = {}
|
||||
local monitor_master_set = {}
|
||||
|
||||
local function read_package(fd)
|
||||
local sz = socket.read(fd, 1)
|
||||
@@ -58,7 +59,7 @@ local function ready()
|
||||
connect_slave(k,v)
|
||||
end
|
||||
for name,address in pairs(globalname) do
|
||||
skynet.redirect(harbor_service, address, "harbor", "N " .. name)
|
||||
skynet.redirect(harbor_service, address, "harbor", 0, "N " .. name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -99,6 +100,9 @@ local function monitor_master(master_fd)
|
||||
end
|
||||
else
|
||||
skynet.error("Master disconnect")
|
||||
for _, v in ipairs(monitor_master_set) do
|
||||
v(true)
|
||||
end
|
||||
socket.close(master_fd)
|
||||
break
|
||||
end
|
||||
@@ -183,6 +187,10 @@ function harbor.LINK(fd, id)
|
||||
end
|
||||
end
|
||||
|
||||
function harbor.LINKMASTER()
|
||||
table.insert(monitor_master_set, skynet.response())
|
||||
end
|
||||
|
||||
function harbor.CONNECT(fd, id)
|
||||
if not slaves[id] then
|
||||
if monitor[id] == nil then
|
||||
|
||||
@@ -33,7 +33,7 @@ local function update(db, key, value, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function wakeup(db, key1, key2, ...)
|
||||
local function wakeup(db, key1, ...)
|
||||
if key1 == nil then
|
||||
return
|
||||
end
|
||||
@@ -43,7 +43,7 @@ local function wakeup(db, key1, key2, ...)
|
||||
end
|
||||
if q[mode] == "queue" then
|
||||
db[key1] = nil
|
||||
if key2 then
|
||||
if select("#", ...) ~= 1 then
|
||||
-- throw error because can't wake up a branch
|
||||
for _,response in ipairs(q) do
|
||||
response(false)
|
||||
@@ -53,7 +53,7 @@ local function wakeup(db, key1, key2, ...)
|
||||
end
|
||||
else
|
||||
-- it's branch
|
||||
return wakeup(q , key2, ...)
|
||||
return wakeup(q , ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -68,18 +68,20 @@ end
|
||||
local function publish(c , source, pack, size)
|
||||
local group = channel[c]
|
||||
if group == nil then
|
||||
-- dead channel, delete the pack
|
||||
mc.bind(pack, 1)
|
||||
-- dead channel, delete the pack. mc.bind returns the pointer in pack
|
||||
local pack = mc.bind(pack, 1)
|
||||
mc.close(pack)
|
||||
return
|
||||
end
|
||||
mc.bind(pack, channel_n[c])
|
||||
local msg = skynet.tostring(pack, size)
|
||||
for k in pairs(group) do
|
||||
-- the msg is a pointer to the real message, publish pointer in local is ok.
|
||||
skynet.redirect(k, source, "multicast", c , msg)
|
||||
end
|
||||
local remote = channel_remote[c]
|
||||
if remote then
|
||||
-- remote publish should unpack the pack, because we should not publish the pointer out.
|
||||
local _, msg, sz = mc.unpack(pack, size)
|
||||
local msg = skynet.tostring(msg,sz)
|
||||
for node in pairs(remote) do
|
||||
|
||||
@@ -4,6 +4,7 @@ local table = table
|
||||
|
||||
local NORET = {}
|
||||
local pool = {}
|
||||
local pool_count = {}
|
||||
local objmap = {}
|
||||
|
||||
local function newobj(name, tbl)
|
||||
@@ -13,6 +14,7 @@ local function newobj(name, tbl)
|
||||
local v = { value = tbl , obj = cobj, watch = {} }
|
||||
objmap[cobj] = v
|
||||
pool[name] = v
|
||||
pool_count[name] = { n = 0, threshold = 16 }
|
||||
end
|
||||
|
||||
local function collectobj()
|
||||
@@ -32,15 +34,18 @@ end
|
||||
|
||||
local CMD = {}
|
||||
|
||||
local env_mt = { __index = _ENV }
|
||||
|
||||
function CMD.new(name, t)
|
||||
local dt = type(t)
|
||||
local value
|
||||
if dt == "table" then
|
||||
value = t
|
||||
elseif dt == "string" then
|
||||
value = {}
|
||||
local f = load(t, "=" .. name, "t", env)
|
||||
value = setmetatable({}, env_mt)
|
||||
local f = load(t, "=" .. name, "t", value)
|
||||
f()
|
||||
setmetatable(value, nil)
|
||||
elseif dt == "nil" then
|
||||
value = {}
|
||||
else
|
||||
@@ -52,10 +57,11 @@ end
|
||||
function CMD.delete(name)
|
||||
local v = assert(pool[name])
|
||||
pool[name] = nil
|
||||
pool_count[name] = nil
|
||||
assert(objmap[v.obj])
|
||||
objmap[v.obj] = true
|
||||
sharedata.host.decref(v.obj)
|
||||
for _,response in ipairs(v.watch) do
|
||||
for _,response in pairs(v.watch) do
|
||||
response(true)
|
||||
end
|
||||
end
|
||||
@@ -83,23 +89,42 @@ function CMD.update(name, t)
|
||||
objmap[oldcobj] = true
|
||||
sharedata.host.decref(oldcobj)
|
||||
pool[name] = nil
|
||||
pool_count[name] = nil
|
||||
end
|
||||
CMD.new(name, t)
|
||||
local newobj = pool[name].obj
|
||||
if watch then
|
||||
sharedata.host.markdirty(oldcobj)
|
||||
for _,response in ipairs(watch) do
|
||||
for _,response in pairs(watch) do
|
||||
response(true, newobj)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_watch(queue)
|
||||
local n = 0
|
||||
for k,response in pairs(queue) do
|
||||
if not response "TEST" then
|
||||
queue[k] = nil
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
function CMD.monitor(name, obj)
|
||||
local v = assert(pool[name])
|
||||
if obj ~= v.obj then
|
||||
return v.obj
|
||||
end
|
||||
|
||||
local n = pool_count[name].n
|
||||
pool_count[name].n = n + 1
|
||||
if n > pool_count[name].threshold then
|
||||
n = n - check_watch(v.watch)
|
||||
pool_count[name].threshold = n * 2
|
||||
end
|
||||
|
||||
table.insert(v.watch, skynet.response())
|
||||
|
||||
return NORET
|
||||
|
||||
@@ -12,10 +12,6 @@ package.path = snax_path .. "?.lua;" .. package.path
|
||||
SERVICE_NAME = snax_name
|
||||
SERVICE_PATH = snax_path
|
||||
|
||||
local mode
|
||||
local thread_id
|
||||
local message_queue = {}
|
||||
local init = false
|
||||
local profile_table = {}
|
||||
|
||||
local function update_stat(name, ti)
|
||||
@@ -38,57 +34,6 @@ local function dispatch(f, ...)
|
||||
return skynet.pack(f(...))
|
||||
end
|
||||
|
||||
local function message_dispatch()
|
||||
while true do
|
||||
if #message_queue==0 then
|
||||
thread_id = coroutine.running()
|
||||
skynet.wait()
|
||||
else
|
||||
local msg = table.remove(message_queue,1)
|
||||
local method = msg.method
|
||||
local f = method[4]
|
||||
if f then
|
||||
if method[2] == "accept" then
|
||||
-- no return
|
||||
profile.start()
|
||||
local ok, data = xpcall(f, traceback, table.unpack(msg))
|
||||
local ti = profile.stop()
|
||||
update_stat(method[3], ti)
|
||||
if not ok then
|
||||
print(string.format("Error on [:%x] to [:%x] %s", msg.source, skynet.self(), tostring(data)))
|
||||
end
|
||||
else
|
||||
profile.start()
|
||||
local ok, data, size = xpcall(dispatch, traceback, f, table.unpack(msg))
|
||||
local ti = profile.stop()
|
||||
update_stat(method[3], ti)
|
||||
if ok then
|
||||
-- skynet.PTYPE_RESPONSE == 1
|
||||
c.send(msg.source, 1, msg.session, data, size)
|
||||
if method[2] == "system" then
|
||||
init = false
|
||||
skynet.exit()
|
||||
break
|
||||
end
|
||||
else
|
||||
-- Can't throw error, so print it directly
|
||||
print(string.format("Error on [:%x] to [:%x] %s", msg.source, skynet.self(), tostring(data)))
|
||||
c.send(msg.source, skynet.PTYPE_ERROR, msg.session, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function queue( session, source, method, ...)
|
||||
table.insert(message_queue, {session = session, source = source, method = method, ... })
|
||||
if thread_id then
|
||||
skynet.wakeup(thread_id)
|
||||
thread_id = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function return_f(f, ...)
|
||||
return skynet.ret(skynet.pack(f(...)))
|
||||
end
|
||||
@@ -108,6 +53,7 @@ local function timing( method, ... )
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
local init = false
|
||||
skynet.dispatch("snax", function ( session , source , id, ...)
|
||||
local method = func[id]
|
||||
|
||||
@@ -119,17 +65,12 @@ skynet.start(function()
|
||||
elseif command == "init" then
|
||||
assert(not init, "Already init")
|
||||
local initfunc = method[4] or function() end
|
||||
mode = initfunc(...)
|
||||
if mode == "queue" then
|
||||
skynet.fork(message_dispatch)
|
||||
end
|
||||
initfunc(...)
|
||||
skynet.ret()
|
||||
skynet.info_func(function()
|
||||
return profile_table
|
||||
end)
|
||||
init = true
|
||||
elseif mode == "queue" then
|
||||
queue( session, source, method , ...)
|
||||
else
|
||||
assert(init, "Never init")
|
||||
assert(command == "exit")
|
||||
@@ -141,11 +82,7 @@ skynet.start(function()
|
||||
end
|
||||
else
|
||||
assert(init, "Init first")
|
||||
if mode == "queue" then
|
||||
queue(session, source, method , ...)
|
||||
else
|
||||
timing(method, ...)
|
||||
end
|
||||
timing(method, ...)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -31,5 +31,17 @@ skynet_harbor_init(int harbor) {
|
||||
|
||||
void
|
||||
skynet_harbor_start(void *ctx) {
|
||||
// the HARBOR must be reserved to ensure the pointer is valid.
|
||||
// It will be released at last by calling skynet_harbor_exit
|
||||
skynet_context_reserve(ctx);
|
||||
REMOTE = ctx;
|
||||
}
|
||||
|
||||
void
|
||||
skynet_harbor_exit() {
|
||||
struct skynet_context * ctx = REMOTE;
|
||||
REMOTE= NULL;
|
||||
if (ctx) {
|
||||
skynet_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,6 @@ void skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int sessio
|
||||
int skynet_harbor_message_isremote(uint32_t handle);
|
||||
void skynet_harbor_init(int harbor);
|
||||
void skynet_harbor_start(void * ctx);
|
||||
void skynet_harbor_exit();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// 1 means mq is in global mq , or the message is dispatching.
|
||||
|
||||
#define MQ_IN_GLOBAL 1
|
||||
#define MQ_OVERLOAD 1024
|
||||
|
||||
struct message_queue {
|
||||
uint32_t handle;
|
||||
@@ -24,17 +25,16 @@ struct message_queue {
|
||||
int lock;
|
||||
int release;
|
||||
int in_global;
|
||||
int overload;
|
||||
int overload_threshold;
|
||||
struct skynet_message *queue;
|
||||
struct message_queue *next;
|
||||
};
|
||||
|
||||
struct global_queue {
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
struct message_queue ** queue;
|
||||
// We use a separated flag array to ensure the mq is pushed.
|
||||
// See the comments below.
|
||||
struct message_queue *list;
|
||||
struct message_queue *head;
|
||||
struct message_queue *tail;
|
||||
int lock;
|
||||
};
|
||||
|
||||
static struct global_queue *Q = NULL;
|
||||
@@ -42,63 +42,36 @@ static struct global_queue *Q = NULL;
|
||||
#define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {}
|
||||
#define UNLOCK(q) __sync_lock_release(&(q)->lock);
|
||||
|
||||
#define GP(p) ((p) % MAX_GLOBAL_MQ)
|
||||
|
||||
void
|
||||
skynet_globalmq_push(struct message_queue * queue) {
|
||||
struct global_queue *q= Q;
|
||||
|
||||
uint32_t tail = GP(__sync_fetch_and_add(&q->tail,1));
|
||||
|
||||
// only one thread can set the slot (change q->queue[tail] from NULL to queue)
|
||||
if (!__sync_bool_compare_and_swap(&q->queue[tail], NULL, queue)) {
|
||||
// The queue may full seldom, save queue in list
|
||||
assert(queue->next == NULL);
|
||||
struct message_queue * last;
|
||||
do {
|
||||
last = q->list;
|
||||
queue->next = last;
|
||||
} while(!__sync_bool_compare_and_swap(&q->list, last, queue));
|
||||
|
||||
return;
|
||||
LOCK(q)
|
||||
assert(queue->next == NULL);
|
||||
if(q->tail) {
|
||||
q->tail->next = queue;
|
||||
q->tail = queue;
|
||||
} else {
|
||||
q->head = q->tail = queue;
|
||||
}
|
||||
UNLOCK(q)
|
||||
}
|
||||
|
||||
struct message_queue *
|
||||
skynet_globalmq_pop() {
|
||||
struct global_queue *q = Q;
|
||||
uint32_t head = q->head;
|
||||
|
||||
if (head == q->tail) {
|
||||
// The queue is empty.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t head_ptr = GP(head);
|
||||
|
||||
struct message_queue * list = q->list;
|
||||
if (list) {
|
||||
// If q->list is not empty, try to load it back to the queue
|
||||
struct message_queue *newhead = list->next;
|
||||
if (__sync_bool_compare_and_swap(&q->list, list, newhead)) {
|
||||
// try load list only once, if success , push it back to the queue.
|
||||
list->next = NULL;
|
||||
skynet_globalmq_push(list);
|
||||
LOCK(q)
|
||||
struct message_queue *mq = q->head;
|
||||
if(mq) {
|
||||
q->head = mq->next;
|
||||
if(q->head == NULL) {
|
||||
assert(mq == q->tail);
|
||||
q->tail = NULL;
|
||||
}
|
||||
mq->next = NULL;
|
||||
}
|
||||
|
||||
struct message_queue * mq = q->queue[head_ptr];
|
||||
if (mq == NULL) {
|
||||
// globalmq push not complete
|
||||
return NULL;
|
||||
}
|
||||
if (!__sync_bool_compare_and_swap(&q->head, head, head+1)) {
|
||||
return NULL;
|
||||
}
|
||||
// only one thread can get the slot (change q->queue[head_ptr] to NULL)
|
||||
if (!__sync_bool_compare_and_swap(&q->queue[head_ptr], mq, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
UNLOCK(q)
|
||||
|
||||
return mq;
|
||||
}
|
||||
@@ -116,6 +89,8 @@ skynet_mq_create(uint32_t handle) {
|
||||
// If the service init success, skynet_context_new will call skynet_mq_force_push to push it to global queue.
|
||||
q->in_global = MQ_IN_GLOBAL;
|
||||
q->release = 0;
|
||||
q->overload = 0;
|
||||
q->overload_threshold = MQ_OVERLOAD;
|
||||
q->queue = skynet_malloc(sizeof(struct skynet_message) * q->cap);
|
||||
q->next = NULL;
|
||||
|
||||
@@ -150,17 +125,42 @@ skynet_mq_length(struct message_queue *q) {
|
||||
return tail + cap - head;
|
||||
}
|
||||
|
||||
int
|
||||
skynet_mq_overload(struct message_queue *q) {
|
||||
if (q->overload) {
|
||||
int overload = q->overload;
|
||||
q->overload = 0;
|
||||
return overload;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
skynet_mq_pop(struct message_queue *q, struct skynet_message *message) {
|
||||
int ret = 1;
|
||||
LOCK(q)
|
||||
|
||||
if (q->head != q->tail) {
|
||||
*message = q->queue[q->head];
|
||||
*message = q->queue[q->head++];
|
||||
ret = 0;
|
||||
if ( ++ q->head >= q->cap) {
|
||||
q->head = 0;
|
||||
int head = q->head;
|
||||
int tail = q->tail;
|
||||
int cap = q->cap;
|
||||
|
||||
if (head >= cap) {
|
||||
q->head = head = 0;
|
||||
}
|
||||
int length = tail - head;
|
||||
if (length < 0) {
|
||||
length += cap;
|
||||
}
|
||||
while (length > q->overload_threshold) {
|
||||
q->overload = length;
|
||||
q->overload_threshold *= 2;
|
||||
}
|
||||
} else {
|
||||
// reset overload_threshold when queue is empty
|
||||
q->overload_threshold = MQ_OVERLOAD;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
@@ -213,8 +213,6 @@ void
|
||||
skynet_mq_init() {
|
||||
struct global_queue *q = skynet_malloc(sizeof(*q));
|
||||
memset(q,0,sizeof(*q));
|
||||
q->queue = skynet_malloc(MAX_GLOBAL_MQ * sizeof(struct message_queue *));
|
||||
memset(q->queue, 0, sizeof(struct message_queue *) * MAX_GLOBAL_MQ);
|
||||
Q=q;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ void skynet_mq_push(struct message_queue *q, struct skynet_message *message);
|
||||
|
||||
// return the length of message queue, for debug
|
||||
int skynet_mq_length(struct message_queue *q);
|
||||
int skynet_mq_overload(struct message_queue *q);
|
||||
|
||||
void skynet_mq_init();
|
||||
|
||||
|
||||
@@ -169,7 +169,11 @@ skynet_context_new(const char * name, const char *param) {
|
||||
int
|
||||
skynet_context_newsession(struct skynet_context *ctx) {
|
||||
// session always be a positive number
|
||||
int session = (++ctx->session_id) & 0x7fffffff;
|
||||
int session = ++ctx->session_id;
|
||||
if (session <= 0) {
|
||||
ctx->session_id = 1;
|
||||
return 1;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -178,6 +182,14 @@ skynet_context_grab(struct skynet_context *ctx) {
|
||||
__sync_add_and_fetch(&ctx->ref,1);
|
||||
}
|
||||
|
||||
void
|
||||
skynet_context_reserve(struct skynet_context *ctx) {
|
||||
skynet_context_grab(ctx);
|
||||
// don't count the context reserved, because skynet abort (the worker threads terminate) only when the total context is 0 .
|
||||
// the reserved context will be release at last.
|
||||
context_dec();
|
||||
}
|
||||
|
||||
static void
|
||||
delete_context(struct skynet_context *ctx) {
|
||||
if (ctx->logfile) {
|
||||
@@ -283,6 +295,10 @@ skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue
|
||||
n = skynet_mq_length(q);
|
||||
n >>= weight;
|
||||
}
|
||||
int overload = skynet_mq_overload(q);
|
||||
if (overload) {
|
||||
skynet_error(ctx, "May overload, message queue length = %d", overload);
|
||||
}
|
||||
|
||||
skynet_monitor_trigger(sm, msg.source , handle);
|
||||
|
||||
@@ -679,7 +695,7 @@ skynet_sendname(struct skynet_context * context, uint32_t source, const char * a
|
||||
if (type & PTYPE_TAG_DONTCOPY) {
|
||||
skynet_free(data);
|
||||
}
|
||||
return session;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
_filter_args(context, type, &session, (void **)&data, &sz);
|
||||
|
||||
@@ -10,6 +10,7 @@ struct skynet_monitor;
|
||||
|
||||
struct skynet_context * skynet_context_new(const char * name, const char * parm);
|
||||
void skynet_context_grab(struct skynet_context *);
|
||||
void skynet_context_reserve(struct skynet_context *ctx);
|
||||
struct skynet_context * skynet_context_release(struct skynet_context *);
|
||||
uint32_t skynet_context_handle(struct skynet_context *);
|
||||
int skynet_context_push(uint32_t handle, struct skynet_message *message);
|
||||
|
||||
@@ -36,10 +36,9 @@ forward_message(int type, bool padding, struct socket_message * result) {
|
||||
int sz = sizeof(*sm);
|
||||
if (padding) {
|
||||
if (result->data) {
|
||||
sz += strlen(result->data) + 1;
|
||||
sz += strlen(result->data);
|
||||
} else {
|
||||
result->data = "";
|
||||
sz += 1;
|
||||
}
|
||||
}
|
||||
sm = (struct skynet_socket_message *)skynet_malloc(sz);
|
||||
@@ -48,7 +47,7 @@ forward_message(int type, bool padding, struct socket_message * result) {
|
||||
sm->ud = result->ud;
|
||||
if (padding) {
|
||||
sm->buffer = NULL;
|
||||
strcpy((char*)(sm+1), result->data);
|
||||
memcpy(sm+1, result->data, sz - sizeof(*sm));
|
||||
} else {
|
||||
sm->buffer = result->data;
|
||||
}
|
||||
@@ -62,6 +61,7 @@ forward_message(int type, bool padding, struct socket_message * result) {
|
||||
if (skynet_context_push((uint32_t)result->opaque, &message)) {
|
||||
// todo: report somewhere to close socket
|
||||
// don't call skynet_socket_close here (It will block mainloop)
|
||||
skynet_free(sm->buffer);
|
||||
skynet_free(sm);
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,9 @@ skynet_socket_poll() {
|
||||
case SOCKET_ACCEPT:
|
||||
forward_message(SKYNET_SOCKET_TYPE_ACCEPT, true, &result);
|
||||
break;
|
||||
case SOCKET_UDP:
|
||||
forward_message(SKYNET_SOCKET_TYPE_UDP, false, &result);
|
||||
break;
|
||||
default:
|
||||
skynet_error(NULL, "Unknown socket message type %d.",type);
|
||||
return -1;
|
||||
@@ -101,9 +104,8 @@ skynet_socket_poll() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
skynet_socket_send(struct skynet_context *ctx, int id, void *buffer, int sz) {
|
||||
int64_t wsz = socket_server_send(SOCKET_SERVER, id, buffer, sz);
|
||||
static int
|
||||
check_wsz(struct skynet_context *ctx, int id, void *buffer, int64_t wsz) {
|
||||
if (wsz < 0) {
|
||||
skynet_free(buffer);
|
||||
return -1;
|
||||
@@ -116,6 +118,12 @@ skynet_socket_send(struct skynet_context *ctx, int id, void *buffer, int sz) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
skynet_socket_send(struct skynet_context *ctx, int id, void *buffer, int sz) {
|
||||
int64_t wsz = socket_server_send(SOCKET_SERVER, id, buffer, sz);
|
||||
return check_wsz(ctx, id, buffer, wsz);
|
||||
}
|
||||
|
||||
void
|
||||
skynet_socket_send_lowpriority(struct skynet_context *ctx, int id, void *buffer, int sz) {
|
||||
socket_server_send_lowpriority(SOCKET_SERVER, id, buffer, sz);
|
||||
@@ -155,3 +163,33 @@ void
|
||||
skynet_socket_nodelay(struct skynet_context *ctx, int id) {
|
||||
socket_server_nodelay(SOCKET_SERVER, id);
|
||||
}
|
||||
|
||||
int
|
||||
skynet_socket_udp(struct skynet_context *ctx, const char * addr, int port) {
|
||||
uint32_t source = skynet_context_handle(ctx);
|
||||
return socket_server_udp(SOCKET_SERVER, source, addr, port);
|
||||
}
|
||||
|
||||
int
|
||||
skynet_socket_udp_connect(struct skynet_context *ctx, int id, const char * addr, int port) {
|
||||
return socket_server_udp_connect(SOCKET_SERVER, id, addr, port);
|
||||
}
|
||||
|
||||
int
|
||||
skynet_socket_udp_send(struct skynet_context *ctx, int id, const char * address, const void *buffer, int sz) {
|
||||
int64_t wsz = socket_server_udp_send(SOCKET_SERVER, id, (const struct socket_udp_address *)address, buffer, sz);
|
||||
return check_wsz(ctx, id, (void *)buffer, wsz);
|
||||
}
|
||||
|
||||
const char *
|
||||
skynet_socket_udp_address(struct skynet_socket_message *msg, int *addrsz) {
|
||||
if (msg->type != SKYNET_SOCKET_TYPE_UDP) {
|
||||
return NULL;
|
||||
}
|
||||
struct socket_message sm;
|
||||
sm.id = msg->id;
|
||||
sm.opaque = 0;
|
||||
sm.ud = msg->ud;
|
||||
sm.data = msg->buffer;
|
||||
return (const char *)socket_server_udp_address(SOCKET_SERVER, &sm, addrsz);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ struct skynet_context;
|
||||
#define SKYNET_SOCKET_TYPE_CLOSE 3
|
||||
#define SKYNET_SOCKET_TYPE_ACCEPT 4
|
||||
#define SKYNET_SOCKET_TYPE_ERROR 5
|
||||
#define SKYNET_SOCKET_TYPE_UDP 6
|
||||
|
||||
struct skynet_socket_message {
|
||||
int type;
|
||||
@@ -30,4 +31,9 @@ void skynet_socket_close(struct skynet_context *ctx, int id);
|
||||
void skynet_socket_start(struct skynet_context *ctx, int id);
|
||||
void skynet_socket_nodelay(struct skynet_context *ctx, int id);
|
||||
|
||||
int skynet_socket_udp(struct skynet_context *ctx, const char * addr, int port);
|
||||
int skynet_socket_udp_connect(struct skynet_context *ctx, int id, const char * addr, int port);
|
||||
int skynet_socket_udp_send(struct skynet_context *ctx, int id, const char * address, const void *buffer, int sz);
|
||||
const char * skynet_socket_udp_address(struct skynet_socket_message *, int *addrsz);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -127,7 +127,6 @@ _worker(void *p) {
|
||||
for (;;) {
|
||||
q = skynet_context_message_dispatch(sm, q, weight);
|
||||
if (q == NULL) {
|
||||
CHECK_ABORT
|
||||
if (pthread_mutex_lock(&m->mutex) == 0) {
|
||||
++ m->sleep;
|
||||
// "spurious wakeup" is harmless,
|
||||
@@ -140,6 +139,7 @@ _worker(void *p) {
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_ABORT
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -232,6 +232,9 @@ skynet_start(struct skynet_config * config) {
|
||||
bootstrap(ctx, config->bootstrap);
|
||||
|
||||
_start(config->thread);
|
||||
|
||||
// harbor_exit may call socket send, so it should exit before socket_free
|
||||
skynet_harbor_exit();
|
||||
skynet_socket_free();
|
||||
if (config->daemon) {
|
||||
daemon_exit(config->daemon);
|
||||
|
||||
@@ -37,27 +37,44 @@
|
||||
|
||||
#define HASH_ID(id) (((unsigned)id) % MAX_SOCKET)
|
||||
|
||||
#define PROTOCOL_TCP 0
|
||||
#define PROTOCOL_UDP 1
|
||||
#define PROTOCOL_UDPv6 2
|
||||
|
||||
#define UDP_ADDRESS_SIZE 19 // ipv6 128bit + port 16bit + 1 byte type
|
||||
|
||||
#define MAX_UDP_PACKAGE 65535
|
||||
|
||||
struct write_buffer {
|
||||
struct write_buffer * next;
|
||||
void *buffer;
|
||||
char *ptr;
|
||||
int sz;
|
||||
void *buffer;
|
||||
bool userobject;
|
||||
uint8_t udp_address[UDP_ADDRESS_SIZE];
|
||||
};
|
||||
|
||||
#define SIZEOF_TCPBUFFER (offsetof(struct write_buffer, udp_address[0]))
|
||||
#define SIZEOF_UDPBUFFER (sizeof(struct write_buffer))
|
||||
|
||||
struct wb_list {
|
||||
struct write_buffer * head;
|
||||
struct write_buffer * tail;
|
||||
};
|
||||
|
||||
struct socket {
|
||||
int fd;
|
||||
int id;
|
||||
int type;
|
||||
int size;
|
||||
int64_t wb_size;
|
||||
uintptr_t opaque;
|
||||
struct wb_list high;
|
||||
struct wb_list low;
|
||||
int64_t wb_size;
|
||||
int fd;
|
||||
int id;
|
||||
uint16_t protocol;
|
||||
uint16_t type;
|
||||
union {
|
||||
int size;
|
||||
uint8_t udp_address[UDP_ADDRESS_SIZE];
|
||||
} p;
|
||||
};
|
||||
|
||||
struct socket_server {
|
||||
@@ -68,9 +85,11 @@ struct socket_server {
|
||||
int alloc_id;
|
||||
int event_n;
|
||||
int event_index;
|
||||
struct socket_object_interface soi;
|
||||
struct event ev[MAX_EVENT];
|
||||
struct socket slot[MAX_SOCKET];
|
||||
char buffer[MAX_INFO];
|
||||
uint8_t udpbuffer[MAX_UDP_PACKAGE];
|
||||
fd_set rfds;
|
||||
};
|
||||
|
||||
@@ -87,6 +106,16 @@ struct request_send {
|
||||
char * buffer;
|
||||
};
|
||||
|
||||
struct request_send_udp {
|
||||
struct request_send send;
|
||||
uint8_t address[UDP_ADDRESS_SIZE];
|
||||
};
|
||||
|
||||
struct request_setudp {
|
||||
int id;
|
||||
uint8_t address[UDP_ADDRESS_SIZE];
|
||||
};
|
||||
|
||||
struct request_close {
|
||||
int id;
|
||||
uintptr_t opaque;
|
||||
@@ -116,17 +145,44 @@ struct request_setopt {
|
||||
int value;
|
||||
};
|
||||
|
||||
struct request_udp {
|
||||
int id;
|
||||
int fd;
|
||||
int family;
|
||||
uintptr_t opaque;
|
||||
};
|
||||
|
||||
/*
|
||||
The first byte is TYPE
|
||||
|
||||
S Start socket
|
||||
B Bind socket
|
||||
L Listen socket
|
||||
K Close socket
|
||||
O Connect to (Open)
|
||||
X Exit
|
||||
D Send package (high)
|
||||
P Send package (low)
|
||||
A Send UDP package
|
||||
T Set opt
|
||||
U Create UDP socket
|
||||
C set udp address
|
||||
*/
|
||||
|
||||
struct request_package {
|
||||
uint8_t header[8]; // 6 bytes dummy
|
||||
union {
|
||||
char buffer[256];
|
||||
struct request_open open;
|
||||
struct request_send send;
|
||||
struct request_send_udp send_udp;
|
||||
struct request_close close;
|
||||
struct request_listen listen;
|
||||
struct request_bind bind;
|
||||
struct request_start start;
|
||||
struct request_setopt setopt;
|
||||
struct request_udp udp;
|
||||
struct request_setudp set_udp;
|
||||
} u;
|
||||
uint8_t dummy[256];
|
||||
};
|
||||
@@ -137,9 +193,40 @@ union sockaddr_all {
|
||||
struct sockaddr_in6 v6;
|
||||
};
|
||||
|
||||
struct send_object {
|
||||
void * buffer;
|
||||
int sz;
|
||||
void (*free_func)(void *);
|
||||
};
|
||||
|
||||
#define MALLOC skynet_malloc
|
||||
#define FREE skynet_free
|
||||
|
||||
static inline bool
|
||||
send_object_init(struct socket_server *ss, struct send_object *so, void *object, int sz) {
|
||||
if (sz < 0) {
|
||||
so->buffer = ss->soi.buffer(object);
|
||||
so->sz = ss->soi.size(object);
|
||||
so->free_func = ss->soi.free;
|
||||
return true;
|
||||
} else {
|
||||
so->buffer = object;
|
||||
so->sz = sz;
|
||||
so->free_func = FREE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_buffer_free(struct socket_server *ss, struct write_buffer *wb) {
|
||||
if (wb->userobject) {
|
||||
ss->soi.free(wb->buffer);
|
||||
} else {
|
||||
FREE(wb->buffer);
|
||||
}
|
||||
FREE(wb);
|
||||
}
|
||||
|
||||
static void
|
||||
socket_keepalive(int fd) {
|
||||
int keepalive = 1;
|
||||
@@ -213,6 +300,7 @@ socket_server_create() {
|
||||
ss->alloc_id = 0;
|
||||
ss->event_n = 0;
|
||||
ss->event_index = 0;
|
||||
memset(&ss->soi, 0, sizeof(ss->soi));
|
||||
FD_ZERO(&ss->rfds);
|
||||
assert(ss->recvctrl_fd < FD_SETSIZE);
|
||||
|
||||
@@ -220,13 +308,12 @@ socket_server_create() {
|
||||
}
|
||||
|
||||
static void
|
||||
free_wb_list(struct wb_list *list) {
|
||||
free_wb_list(struct socket_server *ss, struct wb_list *list) {
|
||||
struct write_buffer *wb = list->head;
|
||||
while (wb) {
|
||||
struct write_buffer *tmp = wb;
|
||||
wb = wb->next;
|
||||
FREE(tmp->buffer);
|
||||
FREE(tmp);
|
||||
write_buffer_free(ss, tmp);
|
||||
}
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
@@ -242,8 +329,8 @@ force_close(struct socket_server *ss, struct socket *s, struct socket_message *r
|
||||
return;
|
||||
}
|
||||
assert(s->type != SOCKET_TYPE_RESERVE);
|
||||
free_wb_list(&s->high);
|
||||
free_wb_list(&s->low);
|
||||
free_wb_list(ss,&s->high);
|
||||
free_wb_list(ss,&s->low);
|
||||
if (s->type != SOCKET_TYPE_PACCEPT && s->type != SOCKET_TYPE_PLISTEN) {
|
||||
sp_del(ss->event_fd, s->fd);
|
||||
}
|
||||
@@ -276,7 +363,7 @@ check_wb_list(struct wb_list *s) {
|
||||
}
|
||||
|
||||
static struct socket *
|
||||
new_fd(struct socket_server *ss, int id, int fd, uintptr_t opaque, bool add) {
|
||||
new_fd(struct socket_server *ss, int id, int fd, int protocol, uintptr_t opaque, bool add) {
|
||||
struct socket * s = &ss->slot[HASH_ID(id)];
|
||||
assert(s->type == SOCKET_TYPE_RESERVE);
|
||||
|
||||
@@ -289,7 +376,8 @@ new_fd(struct socket_server *ss, int id, int fd, uintptr_t opaque, bool add) {
|
||||
|
||||
s->id = id;
|
||||
s->fd = fd;
|
||||
s->size = MIN_READ_BUFFER;
|
||||
s->protocol = protocol;
|
||||
s->p.size = MIN_READ_BUFFER;
|
||||
s->opaque = opaque;
|
||||
s->wb_size = 0;
|
||||
check_wb_list(&s->high);
|
||||
@@ -312,7 +400,7 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
|
||||
struct addrinfo *ai_ptr = NULL;
|
||||
char port[16];
|
||||
sprintf(port, "%d", request->port);
|
||||
memset( &ai_hints, 0, sizeof( ai_hints ) );
|
||||
memset(&ai_hints, 0, sizeof( ai_hints ) );
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
ai_hints.ai_socktype = SOCK_STREAM;
|
||||
ai_hints.ai_protocol = IPPROTO_TCP;
|
||||
@@ -335,7 +423,6 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
sp_nonblocking(sock);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -343,7 +430,7 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
|
||||
goto _failed;
|
||||
}
|
||||
|
||||
ns = new_fd(ss, id, sock, request->opaque, true);
|
||||
ns = new_fd(ss, id, sock, PROTOCOL_TCP, request->opaque, true);
|
||||
if (ns == NULL) {
|
||||
close(sock);
|
||||
goto _failed;
|
||||
@@ -372,7 +459,7 @@ _failed:
|
||||
}
|
||||
|
||||
static int
|
||||
send_list(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_message *result) {
|
||||
send_list_tcp(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_message *result) {
|
||||
while (list->head) {
|
||||
struct write_buffer * tmp = list->head;
|
||||
for (;;) {
|
||||
@@ -396,14 +483,81 @@ send_list(struct socket_server *ss, struct socket *s, struct wb_list *list, stru
|
||||
break;
|
||||
}
|
||||
list->head = tmp->next;
|
||||
FREE(tmp->buffer);
|
||||
FREE(tmp);
|
||||
write_buffer_free(ss,tmp);
|
||||
}
|
||||
list->tail = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static socklen_t
|
||||
udp_socket_address(struct socket *s, const uint8_t udp_address[UDP_ADDRESS_SIZE], union sockaddr_all *sa) {
|
||||
int type = (uint8_t)udp_address[0];
|
||||
if (type != s->protocol)
|
||||
return 0;
|
||||
uint16_t port = 0;
|
||||
memcpy(&port, udp_address+1, sizeof(uint16_t));
|
||||
switch (s->protocol) {
|
||||
case PROTOCOL_UDP:
|
||||
memset(&sa->v4, 0, sizeof(sa->v4));
|
||||
sa->s.sa_family = AF_INET;
|
||||
sa->v4.sin_port = port;
|
||||
memcpy(&sa->v4.sin_addr, udp_address + 1 + sizeof(uint16_t), sizeof(sa->v4.sin_addr)); // ipv4 address is 32 bits
|
||||
return sizeof(sa->v4);
|
||||
case PROTOCOL_UDPv6:
|
||||
memset(&sa->v6, 0, sizeof(sa->v6));
|
||||
sa->s.sa_family = AF_INET6;
|
||||
sa->v6.sin6_port = port;
|
||||
memcpy(&sa->v6.sin6_addr, udp_address + 1 + sizeof(uint16_t), sizeof(sa->v6.sin6_addr)); // ipv4 address is 128 bits
|
||||
return sizeof(sa->v6);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
send_list_udp(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_message *result) {
|
||||
while (list->head) {
|
||||
struct write_buffer * tmp = list->head;
|
||||
union sockaddr_all sa;
|
||||
socklen_t sasz = udp_socket_address(s, tmp->udp_address, &sa);
|
||||
int err = sendto(s->fd, tmp->ptr, tmp->sz, 0, &sa.s, sasz);
|
||||
if (err < 0) {
|
||||
switch(errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "socket-server : udp (%d) sendto error %s.\n",s->id, strerror(errno));
|
||||
return -1;
|
||||
/* // ignore udp sendto error
|
||||
|
||||
result->opaque = s->opaque;
|
||||
result->id = s->id;
|
||||
result->ud = 0;
|
||||
result->data = NULL;
|
||||
|
||||
return SOCKET_ERROR;
|
||||
*/
|
||||
}
|
||||
|
||||
s->wb_size -= tmp->sz;
|
||||
list->head = tmp->next;
|
||||
write_buffer_free(ss,tmp);
|
||||
}
|
||||
list->tail = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
send_list(struct socket_server *ss, struct socket *s, struct wb_list *list, struct socket_message *result) {
|
||||
if (s->protocol == PROTOCOL_TCP) {
|
||||
return send_list_tcp(ss, s, list, result);
|
||||
} else {
|
||||
return send_list_udp(ss, s, list, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
list_uncomplete(struct wb_list *s) {
|
||||
struct write_buffer *wb = s->head;
|
||||
@@ -469,11 +623,13 @@ send_buffer(struct socket_server *ss, struct socket *s, struct socket_message *r
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
append_sendbuffer_(struct wb_list *s, struct request_send * request, int n) {
|
||||
struct write_buffer * buf = MALLOC(sizeof(*buf));
|
||||
buf->ptr = request->buffer+n;
|
||||
buf->sz = request->sz - n;
|
||||
static struct write_buffer *
|
||||
append_sendbuffer_(struct socket_server *ss, struct wb_list *s, struct request_send * request, int size, int n) {
|
||||
struct write_buffer * buf = MALLOC(size);
|
||||
struct send_object so;
|
||||
buf->userobject = send_object_init(ss, &so, request->buffer, request->sz);
|
||||
buf->ptr = (char*)so.buffer+n;
|
||||
buf->sz = so.sz - n;
|
||||
buf->buffer = request->buffer;
|
||||
buf->next = NULL;
|
||||
if (s->head == NULL) {
|
||||
@@ -484,17 +640,27 @@ append_sendbuffer_(struct wb_list *s, struct request_send * request, int n) {
|
||||
s->tail->next = buf;
|
||||
s->tail = buf;
|
||||
}
|
||||
return buf->sz;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline void
|
||||
append_sendbuffer(struct socket *s, struct request_send * request, int n) {
|
||||
s->wb_size += append_sendbuffer_(&s->high, request, n);
|
||||
append_sendbuffer_udp(struct socket_server *ss, struct socket *s, int priority, struct request_send * request, const uint8_t udp_address[UDP_ADDRESS_SIZE]) {
|
||||
struct wb_list *wl = (priority == PRIORITY_HIGH) ? &s->high : &s->low;
|
||||
struct write_buffer *buf = append_sendbuffer_(ss, wl, request, SIZEOF_UDPBUFFER, 0);
|
||||
memcpy(buf->udp_address, udp_address, UDP_ADDRESS_SIZE);
|
||||
s->wb_size += buf->sz;
|
||||
}
|
||||
|
||||
static inline void
|
||||
append_sendbuffer_low(struct socket *s, struct request_send * request) {
|
||||
s->wb_size += append_sendbuffer_(&s->low, request, 0);
|
||||
append_sendbuffer(struct socket_server *ss, struct socket *s, struct request_send * request, int n) {
|
||||
struct write_buffer *buf = append_sendbuffer_(ss, &s->high, request, SIZEOF_TCPBUFFER, n);
|
||||
s->wb_size += buf->sz;
|
||||
}
|
||||
|
||||
static inline void
|
||||
append_sendbuffer_low(struct socket_server *ss,struct socket *s, struct request_send * request) {
|
||||
struct write_buffer *buf = append_sendbuffer_(ss, &s->low, request, SIZEOF_TCPBUFFER, 0);
|
||||
s->wb_size += buf->sz;
|
||||
}
|
||||
|
||||
static inline int
|
||||
@@ -510,41 +676,65 @@ send_buffer_empty(struct socket *s) {
|
||||
Else append package to high (PRIORITY_HIGH) or low (PRIORITY_LOW) list.
|
||||
*/
|
||||
static int
|
||||
send_socket(struct socket_server *ss, struct request_send * request, struct socket_message *result, int priority) {
|
||||
send_socket(struct socket_server *ss, struct request_send * request, struct socket_message *result, int priority, const uint8_t *udp_address) {
|
||||
int id = request->id;
|
||||
struct socket * s = &ss->slot[HASH_ID(id)];
|
||||
struct send_object so;
|
||||
send_object_init(ss, &so, request->buffer, request->sz);
|
||||
if (s->type == SOCKET_TYPE_INVALID || s->id != id
|
||||
|| s->type == SOCKET_TYPE_HALFCLOSE
|
||||
|| s->type == SOCKET_TYPE_PACCEPT) {
|
||||
FREE(request->buffer);
|
||||
so.free_func(request->buffer);
|
||||
return -1;
|
||||
}
|
||||
assert(s->type != SOCKET_TYPE_PLISTEN && s->type != SOCKET_TYPE_LISTEN);
|
||||
if (send_buffer_empty(s) && s->type == SOCKET_TYPE_CONNECTED) {
|
||||
int n = write(s->fd, request->buffer, request->sz);
|
||||
if (n<0) {
|
||||
switch(errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
n = 0;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "socket-server: write to %d (fd=%d) error.",id,s->fd);
|
||||
force_close(ss,s,result);
|
||||
return SOCKET_CLOSE;
|
||||
if (s->protocol == PROTOCOL_TCP) {
|
||||
int n = write(s->fd, so.buffer, so.sz);
|
||||
if (n<0) {
|
||||
switch(errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
n = 0;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "socket-server: write to %d (fd=%d) error :%s.\n",id,s->fd,strerror(errno));
|
||||
force_close(ss,s,result);
|
||||
return SOCKET_CLOSE;
|
||||
}
|
||||
}
|
||||
if (n == so.sz) {
|
||||
so.free_func(request->buffer);
|
||||
return -1;
|
||||
}
|
||||
append_sendbuffer(ss, s, request, n); // add to high priority list, even priority == PRIORITY_LOW
|
||||
} else {
|
||||
// udp
|
||||
if (udp_address == NULL) {
|
||||
udp_address = s->p.udp_address;
|
||||
}
|
||||
union sockaddr_all sa;
|
||||
socklen_t sasz = udp_socket_address(s, udp_address, &sa);
|
||||
int n = sendto(s->fd, so.buffer, so.sz, 0, &sa.s, sasz);
|
||||
if (n != so.sz) {
|
||||
append_sendbuffer_udp(ss,s,priority,request,udp_address);
|
||||
} else {
|
||||
so.free_func(request->buffer);
|
||||
}
|
||||
}
|
||||
if (n == request->sz) {
|
||||
FREE(request->buffer);
|
||||
return -1;
|
||||
}
|
||||
append_sendbuffer(s, request, n); // add to high priority list, even priority == PRIORITY_LOW
|
||||
sp_write(ss->event_fd, s->fd, s, true);
|
||||
} else {
|
||||
if (priority == PRIORITY_LOW) {
|
||||
append_sendbuffer_low(s, request);
|
||||
if (s->protocol == PROTOCOL_TCP) {
|
||||
if (priority == PRIORITY_LOW) {
|
||||
append_sendbuffer_low(ss, s, request);
|
||||
} else {
|
||||
append_sendbuffer(ss, s, request, 0);
|
||||
}
|
||||
} else {
|
||||
append_sendbuffer(s, request, 0);
|
||||
if (udp_address == NULL) {
|
||||
udp_address = s->p.udp_address;
|
||||
}
|
||||
append_sendbuffer_udp(ss,s,priority,request,udp_address);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
@@ -554,7 +744,7 @@ static int
|
||||
listen_socket(struct socket_server *ss, struct request_listen * request, struct socket_message *result) {
|
||||
int id = request->id;
|
||||
int listen_fd = request->fd;
|
||||
struct socket *s = new_fd(ss, id, listen_fd, request->opaque, false);
|
||||
struct socket *s = new_fd(ss, id, listen_fd, PROTOCOL_TCP, request->opaque, false);
|
||||
if (s == NULL) {
|
||||
goto _failed;
|
||||
}
|
||||
@@ -604,7 +794,7 @@ bind_socket(struct socket_server *ss, struct request_bind *request, struct socke
|
||||
result->id = id;
|
||||
result->opaque = request->opaque;
|
||||
result->ud = 0;
|
||||
struct socket *s = new_fd(ss, id, request->fd, request->opaque, true);
|
||||
struct socket *s = new_fd(ss, id, request->fd, PROTOCOL_TCP, request->opaque, true);
|
||||
if (s == NULL) {
|
||||
result->data = NULL;
|
||||
return SOCKET_ERROR;
|
||||
@@ -661,7 +851,7 @@ block_readpipe(int pipefd, void *buffer, int sz) {
|
||||
if (n<0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
fprintf(stderr, "socket-server : read pipe error %s.",strerror(errno));
|
||||
fprintf(stderr, "socket-server : read pipe error %s.\n",strerror(errno));
|
||||
return;
|
||||
}
|
||||
// must atomic read from a pipe
|
||||
@@ -684,6 +874,49 @@ has_cmd(struct socket_server *ss) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
add_udp_socket(struct socket_server *ss, struct request_udp *udp) {
|
||||
int id = udp->id;
|
||||
int protocol;
|
||||
if (udp->family == AF_INET6) {
|
||||
protocol = PROTOCOL_UDPv6;
|
||||
} else {
|
||||
protocol = PROTOCOL_UDP;
|
||||
}
|
||||
struct socket *ns = new_fd(ss, id, udp->fd, protocol, udp->opaque, true);
|
||||
if (ns == NULL) {
|
||||
close(udp->fd);
|
||||
ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
|
||||
}
|
||||
ns->type = SOCKET_TYPE_CONNECTED;
|
||||
memset(ns->p.udp_address, 0, sizeof(ns->p.udp_address));
|
||||
}
|
||||
|
||||
static int
|
||||
set_udp_address(struct socket_server *ss, struct request_setudp *request, struct socket_message *result) {
|
||||
int id = request->id;
|
||||
struct socket *s = &ss->slot[HASH_ID(id)];
|
||||
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
|
||||
return -1;
|
||||
}
|
||||
int type = request->address[0];
|
||||
if (type != s->protocol) {
|
||||
// protocol mismatch
|
||||
result->opaque = s->opaque;
|
||||
result->id = s->id;
|
||||
result->ud = 0;
|
||||
result->data = NULL;
|
||||
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
if (type == PROTOCOL_UDP) {
|
||||
memcpy(s->p.udp_address, request->address, 1+2+4); // 1 type, 2 port, 4 ipv4
|
||||
} else {
|
||||
memcpy(s->p.udp_address, request->address, 1+2+16); // 1 type, 2 port, 16 ipv6
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return type
|
||||
static int
|
||||
ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
|
||||
@@ -714,12 +947,21 @@ ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
|
||||
result->data = NULL;
|
||||
return SOCKET_EXIT;
|
||||
case 'D':
|
||||
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_HIGH);
|
||||
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_HIGH, NULL);
|
||||
case 'P':
|
||||
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_LOW);
|
||||
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_LOW, NULL);
|
||||
case 'A': {
|
||||
struct request_send_udp * rsu = (struct request_send_udp *)buffer;
|
||||
return send_socket(ss, &rsu->send, result, PRIORITY_HIGH, rsu->address);
|
||||
}
|
||||
case 'C':
|
||||
return set_udp_address(ss, (struct request_setudp *)buffer, result);
|
||||
case 'T':
|
||||
setopt_socket(ss, (struct request_setopt *)buffer);
|
||||
return -1;
|
||||
case 'U':
|
||||
add_udp_socket(ss, (struct request_udp *)buffer);
|
||||
return -1;
|
||||
default:
|
||||
fprintf(stderr, "socket-server: Unknown ctrl %c.\n",type);
|
||||
return -1;
|
||||
@@ -730,8 +972,8 @@ ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
|
||||
|
||||
// return -1 (ignore) when error
|
||||
static int
|
||||
forward_message(struct socket_server *ss, struct socket *s, struct socket_message * result) {
|
||||
int sz = s->size;
|
||||
forward_message_tcp(struct socket_server *ss, struct socket *s, struct socket_message * result) {
|
||||
int sz = s->p.size;
|
||||
char * buffer = MALLOC(sz);
|
||||
int n = (int)read(s->fd, buffer, sz);
|
||||
if (n<0) {
|
||||
@@ -762,9 +1004,9 @@ forward_message(struct socket_server *ss, struct socket *s, struct socket_messag
|
||||
}
|
||||
|
||||
if (n == sz) {
|
||||
s->size *= 2;
|
||||
s->p.size *= 2;
|
||||
} else if (sz > MIN_READ_BUFFER && n*2 < sz) {
|
||||
s->size /= 2;
|
||||
s->p.size /= 2;
|
||||
}
|
||||
|
||||
result->opaque = s->opaque;
|
||||
@@ -774,6 +1016,63 @@ forward_message(struct socket_server *ss, struct socket *s, struct socket_messag
|
||||
return SOCKET_DATA;
|
||||
}
|
||||
|
||||
static int
|
||||
gen_udp_address(int protocol, union sockaddr_all *sa, uint8_t * udp_address) {
|
||||
int addrsz = 1;
|
||||
udp_address[0] = (uint8_t)protocol;
|
||||
if (protocol == PROTOCOL_UDP) {
|
||||
memcpy(udp_address+addrsz, &sa->v4.sin_port, sizeof(sa->v4.sin_port));
|
||||
addrsz += sizeof(sa->v4.sin_port);
|
||||
memcpy(udp_address+addrsz, &sa->v4.sin_addr, sizeof(sa->v4.sin_addr));
|
||||
addrsz += sizeof(sa->v4.sin_addr);
|
||||
} else {
|
||||
memcpy(udp_address+addrsz, &sa->v6.sin6_port, sizeof(sa->v6.sin6_port));
|
||||
addrsz += sizeof(sa->v6.sin6_port);
|
||||
memcpy(udp_address+addrsz, &sa->v6.sin6_addr, sizeof(sa->v6.sin6_addr));
|
||||
addrsz += sizeof(sa->v6.sin6_addr);
|
||||
}
|
||||
return addrsz;
|
||||
}
|
||||
|
||||
static int
|
||||
forward_message_udp(struct socket_server *ss, struct socket *s, struct socket_message * result) {
|
||||
union sockaddr_all sa;
|
||||
socklen_t slen = sizeof(sa);
|
||||
int n = recvfrom(s->fd, ss->udpbuffer,MAX_UDP_PACKAGE,0,&sa.s,&slen);
|
||||
if (n<0) {
|
||||
switch(errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
// close when error
|
||||
force_close(ss, s, result);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
uint8_t * data;
|
||||
if (slen == sizeof(sa.v4)) {
|
||||
if (s->protocol != PROTOCOL_UDP)
|
||||
return -1;
|
||||
data = MALLOC(n + 1 + 2 + 4);
|
||||
gen_udp_address(PROTOCOL_UDP, &sa, data + n);
|
||||
} else {
|
||||
if (s->protocol != PROTOCOL_UDPv6)
|
||||
return -1;
|
||||
data = MALLOC(n + 1 + 2 + 16);
|
||||
gen_udp_address(PROTOCOL_UDPv6, &sa, data + n);
|
||||
}
|
||||
memcpy(data, ss->udpbuffer, n);
|
||||
|
||||
result->opaque = s->opaque;
|
||||
result->id = s->id;
|
||||
result->ud = n;
|
||||
result->data = (char *)data;
|
||||
|
||||
return SOCKET_UDP;
|
||||
}
|
||||
|
||||
static int
|
||||
report_connect(struct socket_server *ss, struct socket *s, struct socket_message *result) {
|
||||
int error;
|
||||
@@ -820,7 +1119,7 @@ report_accept(struct socket_server *ss, struct socket *s, struct socket_message
|
||||
}
|
||||
socket_keepalive(client_fd);
|
||||
sp_nonblocking(client_fd);
|
||||
struct socket *ns = new_fd(ss, id, client_fd, s->opaque, false);
|
||||
struct socket *ns = new_fd(ss, id, client_fd, PROTOCOL_TCP, s->opaque, false);
|
||||
if (ns == NULL) {
|
||||
close(client_fd);
|
||||
return 0;
|
||||
@@ -832,7 +1131,10 @@ report_accept(struct socket_server *ss, struct socket *s, struct socket_message
|
||||
result->data = NULL;
|
||||
|
||||
void * sin_addr = (u.s.sa_family == AF_INET) ? (void*)&u.v4.sin_addr : (void *)&u.v6.sin6_addr;
|
||||
if (inet_ntop(u.s.sa_family, sin_addr, ss->buffer, sizeof(ss->buffer))) {
|
||||
int sin_port = ntohs((u.s.sa_family == AF_INET) ? u.v4.sin_port : u.v6.sin6_port);
|
||||
char tmp[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(u.s.sa_family, sin_addr, tmp, sizeof(tmp))) {
|
||||
snprintf(ss->buffer, sizeof(ss->buffer), "%s:%d", tmp, sin_port);
|
||||
result->data = ss->buffer;
|
||||
}
|
||||
|
||||
@@ -902,15 +1204,30 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int
|
||||
fprintf(stderr, "socket-server: invalid socket\n");
|
||||
break;
|
||||
default:
|
||||
if (e->write) {
|
||||
int type = send_buffer(ss, s, result);
|
||||
if (e->read) {
|
||||
int type;
|
||||
if (s->protocol == PROTOCOL_TCP) {
|
||||
type = forward_message_tcp(ss, s, result);
|
||||
} else {
|
||||
type = forward_message_udp(ss, s, result);
|
||||
if (type == SOCKET_UDP) {
|
||||
// try read again
|
||||
--ss->event_index;
|
||||
return SOCKET_UDP;
|
||||
}
|
||||
}
|
||||
if (e->write) {
|
||||
// Try to dispatch write message next step if write flag set.
|
||||
e->read = false;
|
||||
--ss->event_index;
|
||||
}
|
||||
if (type == -1)
|
||||
break;
|
||||
clear_closed_event(ss, result, type);
|
||||
return type;
|
||||
}
|
||||
if (e->read) {
|
||||
int type = forward_message(ss, s, result);
|
||||
if (e->write) {
|
||||
int type = send_buffer(ss, s, result);
|
||||
if (type == -1)
|
||||
break;
|
||||
clear_closed_event(ss, result, type);
|
||||
@@ -943,9 +1260,11 @@ open_request(struct socket_server *ss, struct request_package *req, uintptr_t op
|
||||
int len = strlen(addr);
|
||||
if (len + sizeof(req->u.open) > 256) {
|
||||
fprintf(stderr, "socket-server : Invalid addr %s.\n",addr);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
int id = reserve_id(ss);
|
||||
if (id < 0)
|
||||
return -1;
|
||||
req->u.open.opaque = opaque;
|
||||
req->u.open.id = id;
|
||||
req->u.open.port = port;
|
||||
@@ -959,6 +1278,8 @@ int
|
||||
socket_server_connect(struct socket_server *ss, uintptr_t opaque, const char * addr, int port) {
|
||||
struct request_package request;
|
||||
int len = open_request(ss, &request, opaque, addr, port);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
send_request(ss, &request, 'O', sizeof(request.u.open) + len);
|
||||
return request.u.open.id;
|
||||
}
|
||||
@@ -1009,38 +1330,67 @@ socket_server_close(struct socket_server *ss, uintptr_t opaque, int id) {
|
||||
send_request(ss, &request, 'K', sizeof(request.u.close));
|
||||
}
|
||||
|
||||
// return -1 means failed
|
||||
// or return AF_INET or AF_INET6
|
||||
static int
|
||||
do_bind(const char *host, int port, int protocol, int *family) {
|
||||
int fd;
|
||||
int status;
|
||||
int reuse = 1;
|
||||
struct addrinfo ai_hints;
|
||||
struct addrinfo *ai_list = NULL;
|
||||
char portstr[16];
|
||||
if (host == NULL || host[0] == 0) {
|
||||
host = "0.0.0.0"; // INADDR_ANY
|
||||
}
|
||||
sprintf(portstr, "%d", port);
|
||||
memset( &ai_hints, 0, sizeof( ai_hints ) );
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
if (protocol == IPPROTO_TCP) {
|
||||
ai_hints.ai_socktype = SOCK_STREAM;
|
||||
} else {
|
||||
assert(protocol == IPPROTO_UDP);
|
||||
ai_hints.ai_socktype = SOCK_DGRAM;
|
||||
}
|
||||
ai_hints.ai_protocol = protocol;
|
||||
|
||||
status = getaddrinfo( host, portstr, &ai_hints, &ai_list );
|
||||
if ( status != 0 ) {
|
||||
return -1;
|
||||
}
|
||||
*family = ai_list->ai_family;
|
||||
fd = socket(*family, ai_list->ai_socktype, 0);
|
||||
if (fd < 0) {
|
||||
goto _failed_fd;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int))==-1) {
|
||||
goto _failed;
|
||||
}
|
||||
status = bind(fd, (struct sockaddr *)ai_list->ai_addr, ai_list->ai_addrlen);
|
||||
if (status != 0)
|
||||
goto _failed;
|
||||
|
||||
freeaddrinfo( ai_list );
|
||||
return fd;
|
||||
_failed:
|
||||
close(fd);
|
||||
_failed_fd:
|
||||
freeaddrinfo( ai_list );
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
do_listen(const char * host, int port, int backlog) {
|
||||
// only support ipv4
|
||||
// todo: support ipv6 by getaddrinfo
|
||||
uint32_t addr = INADDR_ANY;
|
||||
if (host[0]) {
|
||||
addr=inet_addr(host);
|
||||
}
|
||||
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
int family = 0;
|
||||
int listen_fd = do_bind(host, port, IPPROTO_TCP, &family);
|
||||
if (listen_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
int reuse = 1;
|
||||
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int))==-1) {
|
||||
goto _failed;
|
||||
}
|
||||
|
||||
struct sockaddr_in my_addr;
|
||||
memset(&my_addr, 0, sizeof(struct sockaddr_in));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(port);
|
||||
my_addr.sin_addr.s_addr = addr;
|
||||
if (bind(listen_fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
|
||||
goto _failed;
|
||||
}
|
||||
if (listen(listen_fd, backlog) == -1) {
|
||||
goto _failed;
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
return listen_fd;
|
||||
_failed:
|
||||
close(listen_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1051,6 +1401,10 @@ socket_server_listen(struct socket_server *ss, uintptr_t opaque, const char * ad
|
||||
}
|
||||
struct request_package request;
|
||||
int id = reserve_id(ss);
|
||||
if (id < 0) {
|
||||
close(fd);
|
||||
return id;
|
||||
}
|
||||
request.u.listen.opaque = opaque;
|
||||
request.u.listen.id = id;
|
||||
request.u.listen.fd = fd;
|
||||
@@ -1062,6 +1416,8 @@ int
|
||||
socket_server_bind(struct socket_server *ss, uintptr_t opaque, int fd) {
|
||||
struct request_package request;
|
||||
int id = reserve_id(ss);
|
||||
if (id < 0)
|
||||
return -1;
|
||||
request.u.bind.opaque = opaque;
|
||||
request.u.bind.id = id;
|
||||
request.u.bind.fd = fd;
|
||||
@@ -1085,3 +1441,130 @@ socket_server_nodelay(struct socket_server *ss, int id) {
|
||||
request.u.setopt.value = 1;
|
||||
send_request(ss, &request, 'T', sizeof(request.u.setopt));
|
||||
}
|
||||
|
||||
void
|
||||
socket_server_userobject(struct socket_server *ss, struct socket_object_interface *soi) {
|
||||
ss->soi = *soi;
|
||||
}
|
||||
|
||||
// UDP
|
||||
|
||||
int
|
||||
socket_server_udp(struct socket_server *ss, uintptr_t opaque, const char * addr, int port) {
|
||||
int fd;
|
||||
int family;
|
||||
if (port != 0 || addr != NULL) {
|
||||
// bind
|
||||
fd = do_bind(addr, port, IPPROTO_UDP, &family);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
family = AF_INET;
|
||||
fd = socket(family, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sp_nonblocking(fd);
|
||||
|
||||
int id = reserve_id(ss);
|
||||
if (id < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
struct request_package request;
|
||||
request.u.udp.id = id;
|
||||
request.u.udp.fd = fd;
|
||||
request.u.udp.opaque = opaque;
|
||||
request.u.udp.family = family;
|
||||
|
||||
send_request(ss, &request, 'U', sizeof(request.u.udp));
|
||||
return id;
|
||||
}
|
||||
|
||||
int64_t
|
||||
socket_server_udp_send(struct socket_server *ss, int id, const struct socket_udp_address *addr, const void *buffer, int sz) {
|
||||
struct socket * s = &ss->slot[HASH_ID(id)];
|
||||
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct request_package request;
|
||||
request.u.send_udp.send.id = id;
|
||||
request.u.send_udp.send.sz = sz;
|
||||
request.u.send_udp.send.buffer = (char *)buffer;
|
||||
|
||||
const uint8_t *udp_address = (const uint8_t *)addr;
|
||||
int addrsz;
|
||||
switch (udp_address[0]) {
|
||||
case PROTOCOL_UDP:
|
||||
addrsz = 1+2+4; // 1 type, 2 port, 4 ipv4
|
||||
break;
|
||||
case PROTOCOL_UDPv6:
|
||||
addrsz = 1+2+16; // 1 type, 2 port, 16 ipv6
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(request.u.send_udp.address, udp_address, addrsz);
|
||||
|
||||
send_request(ss, &request, 'A', sizeof(request.u.send_udp.send)+addrsz);
|
||||
return s->wb_size;
|
||||
}
|
||||
|
||||
int
|
||||
socket_server_udp_connect(struct socket_server *ss, int id, const char * addr, int port) {
|
||||
int status;
|
||||
struct addrinfo ai_hints;
|
||||
struct addrinfo *ai_list = NULL;
|
||||
char portstr[16];
|
||||
sprintf(portstr, "%d", port);
|
||||
memset( &ai_hints, 0, sizeof( ai_hints ) );
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
ai_hints.ai_socktype = SOCK_DGRAM;
|
||||
ai_hints.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
status = getaddrinfo(addr, portstr, &ai_hints, &ai_list );
|
||||
if ( status != 0 ) {
|
||||
return -1;
|
||||
}
|
||||
struct request_package request;
|
||||
request.u.set_udp.id = id;
|
||||
int protocol;
|
||||
|
||||
if (ai_list->ai_family == AF_INET) {
|
||||
protocol = PROTOCOL_UDP;
|
||||
} else if (ai_list->ai_family == AF_INET6) {
|
||||
protocol = PROTOCOL_UDPv6;
|
||||
} else {
|
||||
freeaddrinfo( ai_list );
|
||||
return -1;
|
||||
}
|
||||
|
||||
int addrsz = gen_udp_address(protocol, (union sockaddr_all *)ai_list->ai_addr, request.u.set_udp.address);
|
||||
|
||||
freeaddrinfo( ai_list );
|
||||
|
||||
send_request(ss, &request, 'C', sizeof(request.u.set_udp) - sizeof(request.u.set_udp.address) +addrsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct socket_udp_address *
|
||||
socket_server_udp_address(struct socket_server *ss, struct socket_message *msg, int *addrsz) {
|
||||
uint8_t * address = (uint8_t *)(msg->data + msg->ud);
|
||||
int type = address[0];
|
||||
switch(type) {
|
||||
case PROTOCOL_UDP:
|
||||
*addrsz = 1+2+4;
|
||||
break;
|
||||
case PROTOCOL_UDPv6:
|
||||
*addrsz = 1+2+16;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return (const struct socket_udp_address *)address;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define SOCKET_ACCEPT 3
|
||||
#define SOCKET_ERROR 4
|
||||
#define SOCKET_EXIT 5
|
||||
#define SOCKET_UDP 6
|
||||
|
||||
struct socket_server;
|
||||
|
||||
@@ -36,6 +37,29 @@ int socket_server_listen(struct socket_server *, uintptr_t opaque, const char *
|
||||
int socket_server_connect(struct socket_server *, uintptr_t opaque, const char * addr, int port);
|
||||
int socket_server_bind(struct socket_server *, uintptr_t opaque, int fd);
|
||||
|
||||
// for tcp
|
||||
void socket_server_nodelay(struct socket_server *, int id);
|
||||
|
||||
struct socket_udp_address;
|
||||
|
||||
// create an udp socket handle, attach opaque with it . udp socket don't need call socket_server_start to recv message
|
||||
// if port != 0, bind the socket . if addr == NULL, bind ipv4 0.0.0.0 . If you want to use ipv6, addr can be "::" and port 0.
|
||||
int socket_server_udp(struct socket_server *, uintptr_t opaque, const char * addr, int port);
|
||||
// set default dest address, return 0 when success
|
||||
int socket_server_udp_connect(struct socket_server *, int id, const char * addr, int port);
|
||||
// If the socket_udp_address is NULL, use last call socket_server_udp_connect address instead
|
||||
// You can also use socket_server_send
|
||||
int64_t socket_server_udp_send(struct socket_server *, int id, const struct socket_udp_address *, const void *buffer, int sz);
|
||||
// extract the address of the message, struct socket_message * should be SOCKET_UDP
|
||||
const struct socket_udp_address * socket_server_udp_address(struct socket_server *, struct socket_message *, int *addrsz);
|
||||
|
||||
struct socket_object_interface {
|
||||
void * (*buffer)(void *);
|
||||
int (*size)(void *);
|
||||
void (*free)(void *);
|
||||
};
|
||||
|
||||
// if you send package sz == -1, use soi.
|
||||
void socket_server_userobject(struct socket_server *, struct socket_object_interface *soi);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local skynet = require "skynet"
|
||||
local queue = require "skynet.queue"
|
||||
local snax = require "snax"
|
||||
|
||||
local i = 0
|
||||
local hello = "hello"
|
||||
@@ -32,6 +33,10 @@ function accept.hello()
|
||||
end)
|
||||
end
|
||||
|
||||
function accept.exit(...)
|
||||
snax.exit(...)
|
||||
end
|
||||
|
||||
function response.error()
|
||||
error "throw an error"
|
||||
end
|
||||
@@ -40,9 +45,6 @@ function init( ... )
|
||||
print ("ping server start:", ...)
|
||||
-- init queue
|
||||
lock = queue()
|
||||
|
||||
-- You can return "queue" for queue service mode
|
||||
-- return "queue"
|
||||
end
|
||||
|
||||
function exit(...)
|
||||
|
||||
10
test/testdeadloop.lua
Normal file
10
test/testdeadloop.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
local skynet = require "skynet"
|
||||
local function dead_loop()
|
||||
while true do
|
||||
skynet.sleep(0)
|
||||
end
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
skynet.fork(dead_loop)
|
||||
end)
|
||||
90
test/testmongodb.lua
Normal file
90
test/testmongodb.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
local skynet = require "skynet"
|
||||
local mongo = require "mongo"
|
||||
local bson = require "bson"
|
||||
|
||||
local host, db_name = ...
|
||||
|
||||
function test_insert_without_index()
|
||||
local db = mongo.client({host = host})
|
||||
|
||||
db[db_name].testdb:dropIndex("*")
|
||||
db[db_name].testdb:drop()
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1});
|
||||
assert(ret and ret.n == 1)
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1});
|
||||
assert(ret and ret.n == 1)
|
||||
end
|
||||
|
||||
function test_insert_with_index()
|
||||
local db = mongo.client({host = host})
|
||||
|
||||
db[db_name].testdb:dropIndex("*")
|
||||
db[db_name].testdb:drop()
|
||||
|
||||
db[db_name].testdb:ensureIndex({test_key = 1}, {unique = true, name = "test_key_index"})
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1})
|
||||
assert(ret and ret.n == 1)
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1})
|
||||
assert(ret and ret.n == 0)
|
||||
end
|
||||
|
||||
function test_find_and_remove()
|
||||
local db = mongo.client({host = host})
|
||||
|
||||
db[db_name].testdb:dropIndex("*")
|
||||
db[db_name].testdb:drop()
|
||||
|
||||
db[db_name].testdb:ensureIndex({test_key = 1}, {unique = true, name = "test_key_index"})
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1})
|
||||
assert(ret and ret.n == 1)
|
||||
|
||||
local ret = db[db_name].testdb:findOne({test_key = 1})
|
||||
assert(ret and ret.test_key == 1)
|
||||
|
||||
db[db_name].testdb:delete({test_key = 1})
|
||||
|
||||
local ret = db[db_name].testdb:findOne({test_key = 1})
|
||||
assert(ret == nil)
|
||||
end
|
||||
|
||||
|
||||
function test_expire_index()
|
||||
local db = mongo.client({host = host})
|
||||
|
||||
db[db_name].testdb:dropIndex("*")
|
||||
db[db_name].testdb:drop()
|
||||
|
||||
db[db_name].testdb:ensureIndex({test_key = 1}, {unique = true, name = "test_key_index", expireAfterSeconds = 1, })
|
||||
db[db_name].testdb:ensureIndex({test_date = 1}, {expireAfterSeconds = 1, })
|
||||
|
||||
local ret = db[db_name].testdb:safe_insert({test_key = 1, test_date = bson.date(os.time())})
|
||||
assert(ret and ret.n == 1)
|
||||
|
||||
local ret = db[db_name].testdb:findOne({test_key = 1})
|
||||
assert(ret and ret.test_key == 1)
|
||||
|
||||
for i = 1, 1000 do
|
||||
skynet.sleep(11);
|
||||
|
||||
local ret = db[db_name].testdb:findOne({test_key = 1})
|
||||
if ret == nil then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
assert(false, "test expire index failed");
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
test_insert_without_index()
|
||||
test_insert_with_index()
|
||||
test_find_and_remove()
|
||||
test_expire_index()
|
||||
|
||||
print("mongodb test finish.");
|
||||
end)
|
||||
129
test/testmysql.lua
Normal file
129
test/testmysql.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
local skynet = require "skynet"
|
||||
local mysql = require "mysql"
|
||||
|
||||
local function dump(obj)
|
||||
local getIndent, quoteStr, wrapKey, wrapVal, dumpObj
|
||||
getIndent = function(level)
|
||||
return string.rep("\t", level)
|
||||
end
|
||||
quoteStr = function(str)
|
||||
return '"' .. string.gsub(str, '"', '\\"') .. '"'
|
||||
end
|
||||
wrapKey = function(val)
|
||||
if type(val) == "number" then
|
||||
return "[" .. val .. "]"
|
||||
elseif type(val) == "string" then
|
||||
return "[" .. quoteStr(val) .. "]"
|
||||
else
|
||||
return "[" .. tostring(val) .. "]"
|
||||
end
|
||||
end
|
||||
wrapVal = function(val, level)
|
||||
if type(val) == "table" then
|
||||
return dumpObj(val, level)
|
||||
elseif type(val) == "number" then
|
||||
return val
|
||||
elseif type(val) == "string" then
|
||||
return quoteStr(val)
|
||||
else
|
||||
return tostring(val)
|
||||
end
|
||||
end
|
||||
dumpObj = function(obj, level)
|
||||
if type(obj) ~= "table" then
|
||||
return wrapVal(obj)
|
||||
end
|
||||
level = level + 1
|
||||
local tokens = {}
|
||||
tokens[#tokens + 1] = "{"
|
||||
for k, v in pairs(obj) do
|
||||
tokens[#tokens + 1] = getIndent(level) .. wrapKey(k) .. " = " .. wrapVal(v, level) .. ","
|
||||
end
|
||||
tokens[#tokens + 1] = getIndent(level - 1) .. "}"
|
||||
return table.concat(tokens, "\n")
|
||||
end
|
||||
return dumpObj(obj, 0)
|
||||
end
|
||||
|
||||
local function test2( db)
|
||||
local i=1
|
||||
while true do
|
||||
local res = db:query("select * from cats order by id asc")
|
||||
print ( "test2 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
res = db:query("select * from cats order by id asc")
|
||||
print ( "test2 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
|
||||
skynet.sleep(1000)
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
local function test3( db)
|
||||
local i=1
|
||||
while true do
|
||||
local res = db:query("select * from cats order by id asc")
|
||||
print ( "test3 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
res = db:query("select * from cats order by id asc")
|
||||
print ( "test3 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
skynet.sleep(1000)
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
skynet.start(function()
|
||||
|
||||
local db=mysql.connect{
|
||||
host="127.0.0.1",
|
||||
port=3306,
|
||||
database="skynet",
|
||||
user="root",
|
||||
password="1",
|
||||
max_packet_size = 1024 * 1024
|
||||
}
|
||||
if not db then
|
||||
print("failed to connect")
|
||||
end
|
||||
print("testmysql success to connect to mysql server")
|
||||
|
||||
db:query("set names utf8")
|
||||
|
||||
local res = db:query("drop table if exists cats")
|
||||
res = db:query("create table cats "
|
||||
.."(id serial primary key, ".. "name varchar(5))")
|
||||
print( dump( res ) )
|
||||
|
||||
res = db:query("insert into cats (name) "
|
||||
.. "values (\'Bob\'),(\'\'),(null)")
|
||||
print ( dump( res ) )
|
||||
|
||||
res = db:query("select * from cats order by id asc")
|
||||
print ( dump( res ) )
|
||||
|
||||
-- test in another coroutine
|
||||
skynet.fork( test2, db)
|
||||
skynet.fork( test3, db)
|
||||
-- multiresultset test
|
||||
res = db:query("select * from cats order by id asc ; select * from cats")
|
||||
print ("multiresultset test result=", dump( res ) )
|
||||
|
||||
print ("escape string test result=", mysql.quote_sql_str([[\mysql escape %string test'test"]]) )
|
||||
|
||||
-- bad sql statement
|
||||
local res = db:query("select * from notexisttable" )
|
||||
print( "bad query test result=" ,dump(res) )
|
||||
|
||||
local i=1
|
||||
while true do
|
||||
local res = db:query("select * from cats order by id asc")
|
||||
print ( "test1 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
|
||||
res = db:query("select * from cats order by id asc")
|
||||
print ( "test1 loop times=" ,i,"\n","query result=",dump( res ) )
|
||||
|
||||
|
||||
skynet.sleep(1000)
|
||||
i=i+1
|
||||
end
|
||||
|
||||
--db:disconnect()
|
||||
--skynet.exit()
|
||||
end)
|
||||
|
||||
44
test/testoverload.lua
Normal file
44
test/testoverload.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
local skynet = require "skynet"
|
||||
|
||||
local mode = ...
|
||||
|
||||
if mode == "slave" then
|
||||
|
||||
local CMD = {}
|
||||
|
||||
function CMD.sum(n)
|
||||
skynet.error("for loop begin")
|
||||
local s = 0
|
||||
for i = 1, n do
|
||||
s = s + i
|
||||
end
|
||||
skynet.error("for loop end")
|
||||
end
|
||||
|
||||
function CMD.blackhole()
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
skynet.dispatch("lua", function(_,_, cmd, ...)
|
||||
local f = CMD[cmd]
|
||||
f(...)
|
||||
end)
|
||||
end)
|
||||
|
||||
else
|
||||
|
||||
skynet.start(function()
|
||||
local slave = skynet.newservice(SERVICE_NAME, "slave")
|
||||
for step = 1, 20 do
|
||||
skynet.error("overload test ".. step)
|
||||
for i = 1, 512 * step do
|
||||
skynet.send(slave, "lua", "blackhole")
|
||||
end
|
||||
skynet.sleep(step)
|
||||
end
|
||||
local n = 1000000000
|
||||
skynet.error(string.format("endless test n=%d", n))
|
||||
skynet.send(slave, "lua", "sum", n)
|
||||
end)
|
||||
|
||||
end
|
||||
@@ -2,7 +2,7 @@ local skynet = require "skynet"
|
||||
local snax = require "snax"
|
||||
|
||||
skynet.start(function()
|
||||
local ps = snax.uniqueservice ("pingserver", "hello world")
|
||||
local ps = snax.newservice ("pingserver", "hello world")
|
||||
print(ps.req.ping("foobar"))
|
||||
print(ps.post.hello())
|
||||
print(pcall(ps.req.error))
|
||||
@@ -31,6 +31,6 @@ end
|
||||
print(string.format("%s\tcount:%d time:%f", name, v.count, v.time))
|
||||
end
|
||||
|
||||
print(snax.kill(ps,"exit"))
|
||||
print(ps.post.exit("exit")) -- == snax.kill(ps, "exit")
|
||||
skynet.exit()
|
||||
end)
|
||||
|
||||
15
test/testterm.lua
Normal file
15
test/testterm.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
local skynet = require "skynet"
|
||||
|
||||
local function term()
|
||||
skynet.error("Sleep one second, and term the call to UNEXIST")
|
||||
skynet.sleep(100)
|
||||
local self = skynet.self()
|
||||
skynet.send(skynet.self(), "debug", "TERM", "UNEXIST")
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
skynet.fork(term)
|
||||
skynet.error("call an unexist named service UNEXIST, may block")
|
||||
pcall(skynet.call, "UNEXIST", "lua", "test")
|
||||
skynet.error("unblock the unexisted service call")
|
||||
end)
|
||||
26
test/testudp.lua
Normal file
26
test/testudp.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
local skynet = require "skynet"
|
||||
local socket = require "socket"
|
||||
|
||||
local function server()
|
||||
local host
|
||||
host = socket.udp(function(data, sz, from)
|
||||
local str = skynet.tostring(data,sz) -- skynet.tostring should call only once, because it will free the pointer data
|
||||
print("server recv", str, socket.udp_address(from))
|
||||
socket.sendto(host, from, "OK " .. str)
|
||||
end , "127.0.0.1", 8765) -- bind an address
|
||||
end
|
||||
|
||||
local function client()
|
||||
local c = socket.udp(function(data, sz, from)
|
||||
print("client recv", skynet.tostring(data,sz), socket.udp_address(from))
|
||||
end)
|
||||
socket.udp_connect(c, "127.0.0.1", 8765)
|
||||
for i=1,20 do
|
||||
socket.write(c, "hello " .. i) -- write to the address by udp_connect binding
|
||||
end
|
||||
end
|
||||
|
||||
skynet.start(function()
|
||||
skynet.fork(server)
|
||||
skynet.fork(client)
|
||||
end)
|
||||
Reference in New Issue
Block a user