Compare commits

...

162 Commits

Author SHA1 Message Date
云风
9bb8099008 Merge pull request #160 from cloudwu/dev
release v0.6.1
2014-08-25 10:15:34 +08:00
Cloud Wu
98630badd5 update history for release v0.6.1 2014-08-25 10:13:04 +08:00
Cloud Wu
d0c4a4012a clear socket when connect failed 2014-08-22 15:41:05 +08:00
Cloud Wu
04688e98a7 improve seri lib 2014-08-20 23:34:23 +08:00
Cloud Wu
e0feea9995 fix issue #156 2014-08-19 23:44:07 +08:00
Cloud Wu
55ea556060 avoid issue #154 2014-08-19 23:26:42 +08:00
Cloud Wu
4803e50977 fix error log 2014-08-19 20:52:43 +08:00
Cloud Wu
f393b64124 remove watch from service_mgr 2014-08-18 17:40:18 +08:00
Cloud Wu
9ea756ce4f fix typo 2014-08-18 16:49:14 +08:00
Cloud Wu
f9438461eb check dest type 2014-08-18 16:44:19 +08:00
Cloud Wu
0fb23f9ab8 remove default config name 2014-08-18 16:23:49 +08:00
Cloud Wu
0390818a02 release v0.6.0 2014-08-18 11:31:05 +08:00
云风
e002796d2e Merge pull request #153 from cloudwu/dev
prerelease v0.6.0
2014-08-18 11:28:18 +08:00
Cloud Wu
b8a0e35f21 bugfix: mongo driver 2014-08-16 15:19:01 +08:00
Cloud Wu
bd52c5fece bugfix: socketchannel connect once 2014-08-14 17:03:09 +08:00
Cloud Wu
d2cea9b70f socketchannel request only try connect once 2014-08-14 13:49:49 +08:00
Cloud Wu
7e24d46047 clusterproxy 2014-08-14 11:11:01 +08:00
Cloud Wu
8078d777ab split skynet.debug to skynet.inject 2014-08-13 21:24:48 +08:00
Cloud Wu
939ef564ce debug run extract proto's upvalues 2014-08-13 21:12:07 +08:00
Cloud Wu
6bf8dd9a31 add debug command inject 2014-08-13 21:02:56 +08:00
Cloud Wu
809e6c3a89 add debug command: exit 2014-08-13 19:37:26 +08:00
Cloud Wu
845acd3314 cluster servicename is not stable, so don't cache address 2014-08-13 19:10:51 +08:00
Cloud Wu
2cf69a6ef0 use localname .simpledb 2014-08-13 18:05:11 +08:00
Cloud Wu
978e010e67 add cluster.proxy and cluster.ncall 2014-08-13 17:49:30 +08:00
Cloud Wu
cde423cb73 add skynet.harbor.queryname 2014-08-13 14:32:28 +08:00
Cloud Wu
cfe8b19c8a change .slave to .cslave 2014-08-12 21:41:28 +08:00
Cloud Wu
f69f6e8e70 add stm object support. (STM : Software transactional memory) 2014-08-12 14:33:36 +08:00
Cloud Wu
d7b228f342 use .core for lua c module name 2014-08-11 15:45:16 +08:00
Cloud Wu
0954a906f4 skynet.redirect support string address 2014-08-11 15:29:48 +08:00
Cloud Wu
77d53cf5f1 release v0.5.2 2014-08-11 09:45:37 +08:00
云风
5621c52a44 Merge pull request #150 from cloudwu/http
Http
2014-08-11 09:43:10 +08:00
Cloud Wu
f6fa6c7ded timer support more than 497 days 2014-08-08 20:33:38 +08:00
Cloud Wu
023e4abd87 reset current_point when time error 2014-08-08 15:38:06 +08:00
Cloud Wu
cab10edd1d snaxd change add package.path 2014-08-07 19:34:48 +08:00
Cloud Wu
87a02ffe72 don't check imported function in snax.hotfix 2014-08-07 18:06:08 +08:00
Cloud Wu
e739df0a32 fix log 2014-08-04 18:17:35 +08:00
Cloud Wu
b18962929b bugfix: chunked mode 2014-08-04 18:11:11 +08:00
Cloud Wu
dec2e6fe4c add httpc 2014-08-04 18:06:16 +08:00
Cloud Wu
7beed39b1d bugfix: use temp array for each request 2014-08-04 16:34:29 +08:00
Cloud Wu
ae5a9bb855 Merge branch 'master' into dev 2014-08-04 14:23:13 +08:00
Cloud Wu
4673344e1e update readme, add wiki link 2014-08-02 22:02:17 +08:00
Cloud Wu
c48e0e4c32 Merge branch 'master' into dev 2014-08-02 18:33:08 +08:00
Cloud Wu
b49fc291a7 bugfix: delete local channel 2014-08-02 18:32:54 +08:00
Cloud Wu
0747a7d7e1 remove register_slave 2014-07-31 20:30:22 +08:00
Cloud Wu
4b0a648b21 Merge branch 'master' into dev 2014-07-31 20:13:57 +08:00
Cloud Wu
11c528987c bugfix: socket.read(fd, nil) should clear buffer size to 0 2014-07-31 17:22:59 +08:00
Cloud Wu
40519e9ee1 add response test mode 2014-07-30 14:54:18 +08:00
Cloud Wu
ce5adba5b2 add skynet.response for delay response 2014-07-30 12:10:30 +08:00
Cloud Wu
c78466b486 remove debug print 2014-07-29 21:56:36 +08:00
Cloud Wu
deed51d309 fix typo 2014-07-29 21:30:18 +08:00
Cloud Wu
68a952ff17 bugfix: update until delete 2014-07-29 21:22:49 +08:00
Cloud Wu
434cd1ca79 add sharedata 2014-07-29 20:56:35 +08:00
Cloud Wu
8e09fad233 remove unused skynet_context_init 2014-07-29 10:31:45 +08:00
Cloud Wu
25a8b3a179 release 0.5.0 2014-07-28 15:45:49 +08:00
云风
c323adb8d8 Merge pull request #144 from cloudwu/dev
pre release 0.5.0
2014-07-28 15:44:46 +08:00
Cloud Wu
7a4c7bc8a2 Merge branch 'master' of github.com:cloudwu/skynet into dev 2014-07-25 21:11:09 +08:00
Cloud Wu
a56b6aa425 bugfix: don't use string.format to concat binary string 2014-07-25 21:10:11 +08:00
云风
eaaf57ee0e Merge pull request #141 from duidui007/master
Update lua-bson.c
2014-07-24 11:56:04 +08:00
zeYang
7babdd3a97 Update lua-bson.c
//8  thx!
2014-07-23 21:26:25 +08:00
Cloud Wu
19e6462376 httpd.write_response capture socket error 2014-07-23 14:27:42 +08:00
云风
861f53858f Merge pull request #140 from cloudwu/http
Http
2014-07-23 14:08:02 +08:00
Cloud Wu
db25d1acc6 update history 2014-07-23 14:04:32 +08:00
Cloud Wu
0188e263dd recv header all, and then split with \r\n 2014-07-23 13:34:30 +08:00
Cloud Wu
18f20425a0 httpd don't need readline now 2014-07-23 12:19:48 +08:00
Cloud Wu
551d5048c4 simple url parser 2014-07-22 23:04:37 +08:00
Cloud Wu
d2ad63da81 read_request must return header table 2014-07-22 21:52:18 +08:00
Cloud Wu
932b2943dc simple httpd 2014-07-22 21:38:44 +08:00
Cloud Wu
362d6822bf remove dup line 2014-07-21 14:37:22 +08:00
Cloud Wu
7bdbdcd054 update history 2014-07-21 14:33:29 +08:00
云风
cb7058b563 Merge pull request #138 from cloudwu/config
config can read ENV
2014-07-21 14:33:01 +08:00
Cloud Wu
27ac1642e4 update history 2014-07-21 14:29:11 +08:00
Cloud Wu
d9d2a7d2ce Merge branch 'mongors' into dev 2014-07-21 14:28:09 +08:00
Cloud Wu
39ff6eb2a6 space to tab 2014-07-21 14:27:50 +08:00
云风
b933046fb9 Merge pull request #137 from bttscut/mongors
mongo driver support rs
2014-07-21 14:23:33 +08:00
btt
e59b6b44a5 mongo driver support rs 2014-07-21 11:47:38 +08:00
Cloud Wu
a8b80cd73e simplify clientsocket lib 2014-07-19 14:22:24 +08:00
Cloud Wu
d41c9db019 bugfix: update the response fd, when the client reconnect 2014-07-18 12:09:05 +08:00
Cloud Wu
e1ca48603e simplify code 2014-07-17 22:00:48 +08:00
Cloud Wu
8e4e9ed5c1 bugfix: connect auth 2014-07-17 21:58:09 +08:00
Cloud Wu
a0b536718c config can read ENV 2014-07-17 21:41:08 +08:00
Cloud Wu
6bafd05c65 don't support ip:port 2014-07-17 17:35:36 +08:00
Cloud Wu
7d2d107518 bugfix: socketchannel auth 2014-07-17 17:06:47 +08:00
Cloud Wu
75b2feff73 fix typo 2014-07-16 17:53:53 +08:00
Cloud Wu
9421435ac5 remove dup code 2014-07-16 17:53:18 +08:00
Cloud Wu
1181730302 socketchannel backup support default port 2014-07-16 17:21:39 +08:00
Cloud Wu
acc175e821 socketchannel backup support host/port 2014-07-16 17:18:56 +08:00
Cloud Wu
0ae1fcf7cd add socketchannel backup host 2014-07-16 13:46:44 +08:00
Cloud Wu
37a46607ce add socketchannel:changhost 2014-07-16 12:17:55 +08:00
Cloud Wu
691bb28163 update comment 2014-07-16 12:08:42 +08:00
Cloud Wu
dbf492b761 add snax.gateserver snax.loginserver snax.msgserver 2014-07-15 12:05:58 +08:00
Cloud Wu
43719de101 Merge branch 'master' into dev 2014-07-14 14:20:48 +08:00
云风
a8a683b48e Merge pull request #135 from cloudwu/dev
release v0.4.2
2014-07-14 14:16:17 +08:00
Cloud Wu
4cdf034f15 release 0.4.2 2014-07-14 14:14:42 +08:00
Cloud Wu
9feaf15b3d add netpack.pack_padding 2014-07-14 14:14:05 +08:00
Cloud Wu
ad58ed8a26 add subid 2014-07-13 21:25:44 +08:00
Cloud Wu
0d1c3a64d1 bugfix 2014-07-13 21:02:23 +08:00
Cloud Wu
e963ee6aba Merge branch 'dev' into sconn 2014-07-13 19:50:03 +08:00
Cloud Wu
68ddeab8fa bugfix: skynet.localname return nil not 0 2014-07-13 19:44:59 +08:00
Cloud Wu
3611bfe1af create gamefw 2014-07-13 19:44:23 +08:00
Cloud Wu
ec50e02777 Merge branch 'dev' into sconn 2014-07-13 19:37:21 +08:00
Cloud Wu
3bc7304609 skynet.localname return nil when the name not exist 2014-07-13 19:37:08 +08:00
Cloud Wu
0cdd71c0c2 msggate example 2014-07-13 19:36:44 +08:00
Cloud Wu
e1674f04c3 add gateserver 2014-07-13 14:54:53 +08:00
Cloud Wu
0dba8ed385 Merge branch 'dev' into sconn 2014-07-13 11:58:45 +08:00
Cloud Wu
2e35f405e7 update history 2014-07-13 11:56:28 +08:00
Cloud Wu
6813fd9ef7 bugfix: skynet.exit redirect error, and add datacenter.wait 2014-07-13 11:55:25 +08:00
Cloud Wu
04cb72d1a8 config.name 2014-07-13 11:50:30 +08:00
Cloud Wu
4284cfc372 loginserver and example 2014-07-13 10:32:21 +08:00
Cloud Wu
e8397348dd Merge branch 'dev' into sconn 2014-07-12 23:25:33 +08:00
Cloud Wu
7b5e62b896 fix typo 2014-07-12 23:24:29 +08:00
Cloud Wu
123d942819 Merge branch 'dev' into sconn 2014-07-12 23:14:25 +08:00
Cloud Wu
a090899bce improve cluster 2014-07-12 23:14:05 +08:00
Cloud Wu
68f4f45168 Merge branch 'dev' into sconn 2014-07-12 20:35:17 +08:00
Cloud Wu
3a5de32ad0 add socket.limit for defence 2014-07-12 20:30:02 +08:00
Cloud Wu
c3e758dc87 add crypt lib 2014-07-12 20:28:36 +08:00
Cloud Wu
54e6d03d36 update readme 2014-07-10 16:57:41 +08:00
Cloud Wu
83289b7612 bugfix: Issue #133 2014-07-09 21:07:26 +08:00
Cloud Wu
b7c12846a0 bugfix: socket channel 2014-07-09 15:59:06 +08:00
Cloud Wu
b5e10b8f9f add skynet.queue 2014-07-09 15:26:34 +08:00
Cloud Wu
0855bf30d9 update history 2014-07-09 14:33:53 +08:00
Cloud Wu
d05859b766 gate support TCP_NODELAY 2014-07-09 12:03:44 +08:00
Cloud Wu
fe5c73b1e8 socket id can't be less then 0 2014-07-09 11:25:28 +08:00
Cloud Wu
35775685c3 add worker dispatch weight 2014-07-08 19:56:30 +08:00
Cloud Wu
e60fb1d722 bugfix: remote send handle destination 2014-07-08 11:01:26 +08:00
云风
4e4dacbb4d Merge pull request #130 from cloudwu/dev
Dev
2014-07-07 20:53:38 +08:00
Cloud Wu
8f8b844bde ready for release 2014-07-07 20:52:34 +08:00
Cloud Wu
54f4d94ba2 bugfix: create queue first 2014-07-07 19:50:31 +08:00
Cloud Wu
4967dc2fce skynet.task return session:traceback 2014-07-07 19:00:22 +08:00
Cloud Wu
ece89a1b49 add new api skynet.task() 2014-07-07 18:29:27 +08:00
Cloud Wu
f874fdc618 throw error when skynet.exit 2014-07-03 17:40:07 +08:00
Cloud Wu
711c04e6a9 bugfix: redirect should pass session (0) 2014-07-03 17:13:47 +08:00
Cloud Wu
1e0189962b bugfix: dead lock when service_harbor exit 2014-06-30 11:31:24 +08:00
Cloud Wu
f4d865a882 release v0.4.0 2014-06-30 10:07:55 +08:00
云风
60b0976253 Merge pull request #129 from cloudwu/dev
pre release v0.4.0
2014-06-30 10:05:30 +08:00
Cloud Wu
2b9efcec3f revert 2014-06-26 15:12:16 +08:00
Cloud Wu
d343f3b374 use atomic operation 2014-06-26 15:06:47 +08:00
Cloud Wu
ceeb9912bf typo 2014-06-26 14:04:57 +08:00
Cloud Wu
be1db23ea3 get now before starttime would be better 2014-06-25 11:59:54 +08:00
Cloud Wu
36cab8e060 add skynet.time() 2014-06-25 11:15:28 +08:00
云风
b1d6f7fd88 Merge pull request #127 from xjdrew/real_bugfix_time
Real bugfix time
2014-06-25 11:00:01 +08:00
xjdrew
e0d8b00226 bugfix: ntp校验时把时间往后调整 2014-06-25 10:53:56 +08:00
xjdrew
1cbe639186 bugfix: time rewind 2014-06-25 00:50:42 +08:00
Cloud Wu
f849c522bb bugfix: silly bug 2014-06-24 21:06:40 +08:00
Cloud Wu
3cadd29796 don't throw error when destination address is invalid 2014-06-24 19:32:15 +08:00
Cloud Wu
db952dc658 report too long message error, instead of assert 2014-06-24 19:22:18 +08:00
Cloud Wu
813e6c764d add assert 2014-06-24 18:30:20 +08:00
Cloud Wu
79b6b80676 bugfix: harbor socket package split 2014-06-24 18:19:04 +08:00
Cloud Wu
d30a206a15 bugfix: typo 2014-06-24 11:51:52 +08:00
Cloud Wu
cc2cffacb7 bugfix: master, don't report offline slave 2014-06-24 11:33:29 +08:00
Cloud Wu
d8b0e977f3 merge history 2014-06-23 11:06:20 +08:00
Cloud Wu
4e2dc64f8f Merge branch 'master' into dev 2014-06-21 18:03:24 +08:00
Cloud Wu
9937081854 Redesign harbor/master/dummy service 2014-06-21 17:01:59 +08:00
Cloud Wu
7137850eb2 restore config_log 2014-06-20 03:02:51 +08:00
Cloud Wu
c3eb5cd202 After connecting, socket can send before connected. remove block connect api 2014-06-20 02:49:48 +08:00
Cloud Wu
1a44bfb09a launch logger in C 2014-06-19 22:12:29 +08:00
Cloud Wu
e4197daa54 minor optimize 2014-06-19 16:29:54 +08:00
Cloud Wu
d0468a39f4 cluster.open support cluster name 2014-06-19 16:25:50 +08:00
Cloud Wu
ad9898a209 add module skynet.harbor 2014-06-19 15:43:41 +08:00
云风
33e51a4de9 Merge pull request #124 from xjdrew/bugfix_redis
optimize redis compose_message, see redistest for changes
2014-06-18 20:30:10 +08:00
xjdrew
4ccb677e84 optimize redis compose_message, see redistest for changes
test case: start redistest
2014-06-18 20:20:45 +08:00
Cloud Wu
942c2d3a67 freebsd can use malloc hook now 2014-06-18 15:10:21 +08:00
Cloud Wu
44bef99f64 use skynet.error instead of print 2014-06-18 15:10:21 +08:00
107 changed files with 6631 additions and 1815 deletions

8
.gitignore vendored
View File

@@ -1,10 +1,10 @@
*.o
*.a
skynet
skynet.pid
/skynet
/skynet.pid
3rd/lua/lua
3rd/lua/luac
cservice
luaclib
/cservice
/luaclib
*.so
*.dSYM

View File

@@ -1,6 +1,80 @@
v0.6.1 (2014-8-25)
-----------
* bugfix: datacenter.wakeup
* change struct msg name to avoid conflict in mac
* improve seri library
v0.6.0 (2014-8-18)
-----------
* add sharedata
* bugfix: service exit before init would not report back
* add skynet.response and check multicall skynet.ret
* skynet.newservice throw error when lanuch faild
* Don't check imported function in snax.hotfix
* snax service add change SERVICE_PATH and add it to package.path
* skynet.redirect support string address
* bugfix: skynet.harbor.link may block
* add skynet.harbor.queryname to query globalname
* add cluster.proxy
* add DEBUG command exit (send a message to lua service by DEBUG)
* add DEBUG command run (debug_console command inject)
* bugfix : socketchannel connect once
* bugfix : mongo driver
v0.5.2 (2014-8-11)
-----------
* Bugfix : httpd request
* Bugifx : http chunked mode
* Add : httpc
* timer support more than 497 days
v0.5.1 (2014-8-4)
-----------
* Bugfix : http module
* Bugfix : multicast local channel delete
* Bugfix : socket.read(fd)
v0.5.0 (2014-7-28)
-----------
* skynet.exit will quit service immediately.
* Add snax.gateserver, snax.loginserver, snax.msgserver
* Simplify clientsocket lib
* mongo driver support replica set
* config file support read from ENV
* add simple httpd (see examples/simpleweb.lua)
v0.4.2 (2014-7-14)
-----------
* Bugfix : invalid negative socket id
* Add optional TCP_NODELAY support
* Add worker thread weight
* Add skynet.queue
* Bugfix: socketchannel
* cluster can throw error
* Add readline and writeline to clientsocket lib
* Add cluster.reload to reload config file
* Add datacenter.wait
v0.4.1 (2014-7-7)
-----------
* Add SERVICE_NAME in loader
* Throw error back when skynet.error
* Add skynet.task
* Bugfix for last version (harbor service bugs)
v0.4.0 (2014-6-30)
-----------
* Optimize redis driver `compose_message`.
* Add module skynet.harbor for monitor harbor connect/disconnect, see test/testharborlink.lua .
* cluster.open support cluster name.
* Add new api skynet.packstring , and skynet.unpack support lua string
* socket.listen support put port into address. (address:port)
* Redesign harbor/master/dummy, remove lots of C code and rewite in lua.
* Remove block connect api, queue sending message during connecting now.
* Add skynet.time()
v0.3.2 (2014-6-23)
----------
* Bugifx : mongo driver and lua-bson . (objectid encoding, and gc problem when mongdo driver reply bson object).
* Bugfix : cluster (double free).
* Add socket.header() to decode big-endian package header (and fix the bug in cluster).

View File

@@ -40,10 +40,10 @@ jemalloc : $(MALLOC_STATICLIB)
# skynet
CSERVICE = snlua logger gate master harbor dummy
CSERVICE = snlua logger gate harbor
LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack \
cjson clientsocket memory profile multicast \
cluster
cluster crypt sharedata stm
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 \
@@ -110,6 +110,15 @@ $(LUA_CLIB_PATH)/multicast.so : lualib-src/lua-multicast.c | $(LUA_CLIB_PATH)
$(LUA_CLIB_PATH)/cluster.so : lualib-src/lua-cluster.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
$(LUA_CLIB_PATH)/crypt.so : lualib-src/lua-crypt.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
$(LUA_CLIB_PATH)/sharedata.so : lualib-src/lua-sharedata.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) $^ -o $@
$(LUA_CLIB_PATH)/stm.so : lualib-src/lua-stm.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@
clean :
rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so

View File

@@ -3,7 +3,7 @@
For linux, install autoconf first for jemalloc
```
git clone git@github.com:cloudwu/skynet.git
git clone https://github.com/cloudwu/skynet.git
cd skynet
make 'PLATFORM' # PLATFORM can be linux, macosx, freebsd now
```
@@ -34,9 +34,6 @@ Each lua file only load once and cache it in memory during skynet start . so if
You can also use the offical lua version , edit the makefile by yourself .
## Blog (in Chinese)
## How To (in Chinese)
* http://blog.codingnow.com/2012/09/the_design_of_skynet.html
* http://blog.codingnow.com/2012/08/skynet.html
* http://blog.codingnow.com/2012/08/skynet_harbor_rpc.html
* http://blog.codingnow.com/eo/skynet/
* Read Wiki https://github.com/cloudwu/skynet/wiki

View File

@@ -2,30 +2,46 @@ package.cpath = "luaclib/?.so"
local socket = require "clientsocket"
local cjson = require "cjson"
local bit32 = require "bit32"
local fd = socket.connect("127.0.0.1", 8888)
local fd = assert(socket.connect("127.0.0.1", 8888))
local last
local result = {}
local function send_package(fd, pack)
local size = #pack
local package = string.char(bit32.extract(size,8,8)) ..
string.char(bit32.extract(size,0,8))..
pack
local function dispatch()
while true do
local status
status, last = socket.recv(fd, last, result)
if status == nil then
error "Server closed"
end
if not status then
break
end
for _, v in ipairs(result) do
local session,t,str = string.match(v, "(%d+)(.)(.*)")
assert(t == '-' or t == '+')
session = tonumber(session)
local result = cjson.decode(str)
print("Response:",session, result[1], result[2])
end
socket.send(fd, package)
end
local function unpack_package(text)
local size = #text
if size < 2 then
return nil, text
end
local s = text:byte(1) * 256 + text:byte(2)
if size < s+2 then
return nil, text
end
return text:sub(3,2+s), text:sub(3+s)
end
local function recv_package(last)
local result
result, last = unpack_package(last)
if result then
return result, last
end
local r = socket.recv(fd)
if not r then
return nil, last
end
if r == "" then
error "Server closed"
end
return unpack_package(last .. r)
end
local session = 0
@@ -33,13 +49,26 @@ local session = 0
local function send_request(v)
session = session + 1
local str = string.format("%d+%s",session, cjson.encode(v))
socket.send(fd, str)
send_package(fd, str)
print("Request:", session)
end
local last = ""
while true do
dispatch()
local cmd = socket.readline()
while true do
local v
v, last = recv_package(last)
if not v then
break
end
local session,t,str = string.match(v, "(%d+)(.)(.*)")
assert(t == '-' or t == '+')
session = tonumber(session)
local result = cjson.decode(str)
print("Response:",session, result[1], result[2])
end
local cmd = socket.readstdin()
if cmd then
local args = {}
string.gsub(cmd, '[^ ]+', function(v) table.insert(args, v) end )

View File

@@ -2,8 +2,9 @@ local skynet = require "skynet"
local cluster = require "cluster"
skynet.start(function()
skynet.newservice("simpledb")
print(skynet.call("SIMPLEDB", "lua", "SET", "a", "foobar"))
print(skynet.call("SIMPLEDB", "lua", "GET", "a"))
cluster.open(2528)
local sdb = skynet.newservice("simpledb")
skynet.name(".simpledb", sdb)
print(skynet.call(".simpledb", "lua", "SET", "a", "foobar"))
print(skynet.call(".simpledb", "lua", "GET", "a"))
cluster.open "db"
end)

View File

@@ -2,5 +2,7 @@ local skynet = require "skynet"
local cluster = require "cluster"
skynet.start(function()
print(cluster.call("db", "SIMPLEDB", "GET", "a"))
local proxy = cluster.proxy("db", ".simpledb")
print(skynet.call(proxy, "lua", "GET", "a"))
print(cluster.call("db", ".simpledb", "GET", "a"))
end)

8
examples/config.login Normal file
View File

@@ -0,0 +1,8 @@
thread = 8
logger = nil
harbor = 0
start = "main"
bootstrap = "snlua bootstrap" -- The service for bootstrap
luaservice = "./service/?.lua;./examples/login/?.lua"
lualoader = "lualib/loader.lua"
cpath = "./cservice/?.so"

View File

@@ -1,8 +1,9 @@
local skynet = require "skynet"
skynet.start(function()
skynet.dispatch("text", function(session, address, text)
print("[GLOBALLOG]", skynet.address(address),text)
skynet.dispatch("lua", function(session, address, ...)
print("[GLOBALLOG]", skynet.address(address), ...)
end)
skynet.register ".log"
skynet.register "LOG"
end)

184
examples/login/client.lua Normal file
View File

@@ -0,0 +1,184 @@
package.cpath = "luaclib/?.so"
local socket = require "clientsocket"
local crypt = require "crypt"
local bit32 = require "bit32"
local fd = assert(socket.connect("127.0.0.1", 8001))
local function writeline(fd, text)
socket.send(fd, text .. "\n")
end
local function unpack_line(text)
local from = text:find("\n", 1, true)
if from then
return text:sub(1, from-1), text:sub(from+1)
end
return nil, text
end
local last = ""
local function unpack_f(f)
local function try_recv(fd, last)
local result
result, last = f(last)
if result then
return result, last
end
local r = socket.recv(fd)
if not r then
return nil, last
end
if r == "" then
error "Server closed"
end
return f(last .. r)
end
return function()
while true do
local result
result, last = try_recv(fd, last)
if result then
return result
end
socket.usleep(100)
end
end
end
local readline = unpack_f(unpack_line)
local challenge = crypt.base64decode(readline())
local clientkey = crypt.randomkey()
writeline(fd, crypt.base64encode(crypt.dhexchange(clientkey)))
local secret = crypt.dhsecret(crypt.base64decode(readline()), clientkey)
print("sceret is ", crypt.hexencode(secret))
local hmac = crypt.hmac64(challenge, secret)
writeline(fd, crypt.base64encode(hmac))
local token = {
server = "sample",
user = "hello",
pass = "password",
}
local function encode_token(token)
return string.format("%s@%s:%s",
crypt.base64encode(token.user),
crypt.base64encode(token.server),
crypt.base64encode(token.pass))
end
local etoken = crypt.desencode(secret, encode_token(token))
local b = crypt.base64encode(etoken)
writeline(fd, crypt.base64encode(etoken))
local result = readline()
print(result)
local code = tonumber(string.sub(result, 1, 3))
assert(code == 200)
socket.close(fd)
local subid = crypt.base64decode(string.sub(result, 5))
print("login ok, subid=", subid)
----- connect to game server
local function send_request(v, session)
local size = #v + 4
local package = string.char(bit32.extract(size,8,8))..
string.char(bit32.extract(size,0,8))..
v..
string.char(bit32.extract(session,24,8))..
string.char(bit32.extract(session,16,8))..
string.char(bit32.extract(session,8,8))..
string.char(bit32.extract(session,0,8))
socket.send(fd, package)
return v, session
end
local function recv_response(v)
local content = v:sub(1,-6)
local ok = v:sub(-5,-5):byte()
local session = 0
for i=-4,-1 do
local c = v:byte(i)
session = session + bit32.lshift(c,(-1-i) * 8)
end
return ok ~=0 , content, session
end
local function unpack_package(text)
local size = #text
if size < 2 then
return nil, text
end
local s = text:byte(1) * 256 + text:byte(2)
if size < s+2 then
return nil, text
end
return text:sub(3,2+s), text:sub(3+s)
end
local readpackage = unpack_f(unpack_package)
local function send_package(fd, pack)
local size = #pack
local package = string.char(bit32.extract(size,8,8))..
string.char(bit32.extract(size,0,8))..
pack
socket.send(fd, package)
end
local text = "echo"
local index = 1
print("connect")
local fd = assert(socket.connect("127.0.0.1", 8888))
last = ""
local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index)
local hmac = crypt.hmac64(crypt.hashkey(handshake), secret)
send_package(fd, handshake .. ":" .. crypt.base64encode(hmac))
print(readpackage())
print("===>",send_request(text,0))
-- don't recv response
-- print("<===",recv_response(readpackage()))
print("disconnect")
socket.close(fd)
index = index + 1
print("connect again")
local fd = assert(socket.connect("127.0.0.1", 8888))
last = ""
local handshake = string.format("%s@%s#%s:%d", crypt.base64encode(token.user), crypt.base64encode(token.server),crypt.base64encode(subid) , index)
local hmac = crypt.hmac64(crypt.hashkey(handshake), secret)
send_package(fd, handshake .. ":" .. crypt.base64encode(hmac))
print(readpackage())
print("===>",send_request("fake",0)) -- request again (use last session 0, so the request message is fake)
print("===>",send_request("again",1)) -- request again (use new session)
print("<===",recv_response(readpackage()))
print("<===",recv_response(readpackage()))
print("disconnect")
socket.close(fd)

88
examples/login/gated.lua Normal file
View File

@@ -0,0 +1,88 @@
local msgserver = require "snax.msgserver"
local crypt = require "crypt"
local skynet = require "skynet"
local loginservice = tonumber(...)
local server = {}
local users = {}
local username_map = {}
local internal_id = 0
-- login server disallow multi login, so login_handler never be reentry
-- call by login server
function server.login_handler(uid, secret)
if users[uid] then
error(string.format("%s is already login", uid))
end
internal_id = internal_id + 1
local username = msgserver.username(uid, internal_id, servername)
-- you can use a pool to alloc new agent
local agent = skynet.newservice "msgagent"
local u = {
username = username,
agent = agent,
uid = uid,
subid = internal_id,
}
-- trash subid (no used)
skynet.call(agent, "lua", "login", uid, internal_id, secret)
users[uid] = u
username_map[username] = u
msgserver.login(username, secret)
-- you should return unique subid
return internal_id
end
-- call by agent
function server.logout_handler(uid, subid)
local u = users[uid]
if u then
local username = msgserver.username(uid, subid, servername)
assert(u.username == username)
msgserver.logout(u.username)
users[uid] = nil
username_map[u.username] = nil
skynet.call(loginservice, "lua", "logout",uid, subid)
end
end
-- call by login server
function server.kick_handler(uid, subid)
local u = users[uid]
if u then
local username = msgserver.username(uid, subid, servername)
assert(u.username == username)
-- NOTICE: logout may call skynet.exit, so you should use pcall.
pcall(skynet.call, u.agent, "lua", "logout")
end
end
-- call by self (when socket disconnect)
function server.disconnect_handler(username)
local u = username_map[username]
if u then
skynet.call(u.agent, "lua", "afk")
end
end
-- call by self (when recv a request from client)
function server.request_handler(username, msg, sz)
local u = username_map[username]
return skynet.tostring(skynet.rawcall(u.agent, "client", msg, sz))
end
-- call by self (when gate open)
function server.register_handler(name)
servername = name
skynet.call(loginservice, "lua", "register_gate", servername, skynet.self())
end
msgserver.start(server)

62
examples/login/logind.lua Normal file
View File

@@ -0,0 +1,62 @@
local login = require "snax.loginserver"
local crypt = require "crypt"
local skynet = require "skynet"
local server = {
host = "127.0.0.1",
port = 8001,
multilogin = false, -- disallow multilogin
name = "login_master",
}
local server_list = {}
local user_online = {}
local user_login = {}
function server.auth_handler(token)
-- the token is base64(user)@base64(server):base64(password)
local user, server, password = token:match("([^@]+)@([^:]+):(.+)")
user = crypt.base64decode(user)
server = crypt.base64decode(server)
password = crypt.base64decode(password)
assert(password == "password")
return server, user
end
function server.login_handler(server, uid, secret)
print(string.format("%s@%s is login, secret is %s", uid, server, crypt.hexencode(secret)))
local gameserver = assert(server_list[server], "Unknown server")
-- only one can login, because disallow multilogin
local last = user_online[uid]
if last then
skynet.call(last.address, "lua", "kick", uid, last.subid)
end
if user_online[uid] then
error(string.format("user %s is already online", uid))
end
local subid = tostring(skynet.call(gameserver, "lua", "login", uid, secret))
user_online[uid] = { address = gameserver, subid = subid , server = server}
return subid
end
local CMD = {}
function CMD.register_gate(server, address)
server_list[server] = address
end
function CMD.logout(uid, subid)
local u = user_online[uid]
if u then
print(string.format("%s@%s is logout", uid, u.server))
user_online[uid] = nil
end
end
function server.command_handler(command, source, ...)
local f = assert(CMD[command])
return f(source, ...)
end
login(server)

12
examples/login/main.lua Normal file
View File

@@ -0,0 +1,12 @@
local skynet = require "skynet"
skynet.start(function()
local loginserver = skynet.newservice("logind")
local gate = skynet.newservice("gated", loginserver)
skynet.call(gate, "lua", "open" , {
port = 8888,
maxclient = 64,
servername = "sample",
})
end)

View File

@@ -0,0 +1,53 @@
local skynet = require "skynet"
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
unpack = skynet.tostring,
}
local gate
local userid, subid
local CMD = {}
function CMD.login(source, uid, sid, secret)
-- you may use secret to make a encrypted data stream
skynet.error(string.format("%s is login", uid))
gate = source
userid = uid
subid = sid
-- you may load user data from database
end
local function logout()
if gate then
skynet.call(gate, "lua", "logout", userid, subid)
end
skynet.exit()
end
function CMD.logout(source)
-- NOTICE: The logout MAY be reentry
skynet.error(string.format("%s is logout", userid))
logout()
end
function CMD.afk(source)
-- the connection is broken, but the user may back
skynet.error(string.format("AFK"))
end
skynet.start(function()
-- If you want to fork a work thread , you MUST do it in CMD.login
skynet.dispatch("lua", function(session, source, command, ...)
local f = assert(CMD[command])
skynet.ret(skynet.pack(f(source, ...)))
end)
skynet.dispatch("client", function(_,_, msg)
-- the simple ehco service
skynet.sleep(10) -- sleep a while
skynet.ret(msg)
end)
end)

View File

@@ -11,7 +11,9 @@ skynet.start(function()
skynet.call(watchdog, "lua", "start", {
port = 8888,
maxclient = max_client,
nodelay = true,
})
print("Watchdog listen on ", 8888)
skynet.exit()
end)

View File

@@ -2,7 +2,6 @@ local skynet = require "skynet"
skynet.start(function()
print("Log server start")
local service = skynet.newservice("service_mgr")
skynet.monitor "simplemonitor"
local log = skynet.newservice("globallog")
skynet.exit()

72
examples/share.lua Normal file
View File

@@ -0,0 +1,72 @@
local skynet = require "skynet"
local sharedata = require "sharedata"
local mode = ...
if mode == "host" then
skynet.start(function()
skynet.error("new foobar")
sharedata.new("foobar", { a=1, b= { "hello", "world" } })
skynet.fork(function()
skynet.sleep(200) -- sleep 2s
skynet.error("update foobar a = 2")
sharedata.update("foobar", { a =2 })
skynet.sleep(200) -- sleep 2s
skynet.error("update foobar a = 3")
sharedata.update("foobar", { a = 3, b = { "change" } })
skynet.sleep(100)
skynet.error("delete foobar")
sharedata.delete "foobar"
end)
end)
else
skynet.start(function()
skynet.newservice(SERVICE_NAME, "host")
local obj = sharedata.query "foobar"
local b = obj.b
skynet.error(string.format("a=%d", obj.a))
for k,v in ipairs(b) do
skynet.error(string.format("b[%d]=%s", k,v))
end
for i = 1, 5 do
skynet.sleep(100)
skynet.error("second " ..i)
for k,v in pairs(obj) do
skynet.error(string.format("%s = %s", k , tostring(v)))
end
end
local ok, err = pcall(function()
local tmp = { b[1], b[2] } -- b is invalid , so pcall should failed
end)
if not ok then
skynet.error(err)
end
-- obj. b is not the same with local b
for k,v in ipairs(obj.b) do
skynet.error(string.format("b[%d] = %s", k , tostring(v)))
end
collectgarbage()
skynet.error("sleep")
skynet.sleep(100)
b = nil
collectgarbage()
skynet.error("sleep")
skynet.sleep(100)
skynet.exit()
end)
end

View File

@@ -16,7 +16,11 @@ end
skynet.start(function()
skynet.dispatch("lua", function(session, address, cmd, ...)
local f = command[string.upper(cmd)]
skynet.ret(skynet.pack(f(...)))
if f then
skynet.ret(skynet.pack(f(...)))
else
error(string.format("Unknown command %s", tostring(cmd)))
end
end)
skynet.register "SIMPLEDB"
end)

View File

@@ -12,7 +12,7 @@ skynet.register_protocol {
local w = service_map[address]
if w then
for watcher in pairs(w) do
skynet.redirect(watcher, address, "error", "")
skynet.redirect(watcher, address, "error", 0, "")
end
service_map[address] = false
end

79
examples/simpleweb.lua Normal file
View File

@@ -0,0 +1,79 @@
local skynet = require "skynet"
local socket = require "socket"
local httpd = require "http.httpd"
local sockethelper = require "http.sockethelper"
local urllib = require "http.url"
local table = table
local string = string
local mode = ...
if mode == "agent" then
local function response(id, ...)
local ok, err = httpd.write_response(sockethelper.writefunc(id), ...)
if not ok then
-- if err == sockethelper.socket_error , that means socket closed.
skynet.error(string.format("fd = %d, %s", id, err))
end
end
skynet.start(function()
skynet.dispatch("lua", function (_,_,id)
socket.start(id)
-- limit request body size to 8192 (you can pass nil to unlimit)
local code, url, method, header, body = httpd.read_request(sockethelper.readfunc(id), 8192)
if code then
if code ~= 200 then
response(id, code)
else
local tmp = {}
if header.host then
table.insert(tmp, string.format("host: %s", header.host))
end
local path, query = urllib.parse(url)
table.insert(tmp, string.format("path: %s", path))
if query then
local q = urllib.parse_query(query)
for k, v in pairs(q) do
table.insert(tmp, string.format("query: %s= %s", k,v))
end
end
table.insert(tmp, "-----header----")
for k,v in pairs(header) do
table.insert(tmp, string.format("%s = %s",k,v))
end
table.insert(tmp, "-----body----\n" .. body)
response(id, code, table.concat(tmp,"\n"))
end
else
if url == sockethelper.socket_error then
skynet.error("socket closed")
else
skynet.error(url)
end
end
socket.close(id)
end)
end)
else
skynet.start(function()
local agent = {}
for i= 1, 20 do
agent[i] = skynet.newservice(SERVICE_NAME, "agent")
end
local balance = 1
local id = socket.listen("0.0.0.0", 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)
balance = balance + 1
if balance > #agent then
balance = 1
end
end)
end)
end

View File

@@ -1083,7 +1083,7 @@ typeclosure(lua_State *L) {
"string", // 5
"binary", // 6
"objectid", // 7
"timestamp",// 8
"timestamp", // 8
"date", // 9
"regex", // 10
"minkey", // 11
@@ -1180,7 +1180,6 @@ luaopen_bson(lua_State *L) {
{ "timestamp", ltimestamp },
{ "regex", lregex },
{ "binary", lbinary },
{ "regex", lregex },
{ "objectid", lobjectid },
{ "decode", ldecode },
{ NULL, NULL },

View File

@@ -75,47 +75,12 @@ lsend(lua_State *L) {
size_t sz = 0;
int fd = luaL_checkinteger(L,1);
const char * msg = luaL_checklstring(L, 2, &sz);
uint8_t tmp[sz + 2];
if (sz >= 0x10000) {
return luaL_error(L, "package too long %d (16bit limited)", (int)sz);
}
tmp[0] = (sz >> 8) & 0xff;
tmp[1] = sz & 0xff;
memcpy(tmp+2, msg, sz);
block_send(L, fd, (const char *)tmp, (int)sz+2);
block_send(L, fd, msg, (int)sz);
return 0;
}
static int
unpack(lua_State *L, uint8_t *buffer, int sz, int n) {
int size = 0;
if (sz >= 2) {
size = buffer[0] << 8 | buffer[1];
if (size > sz - 2) {
goto _block;
}
} else {
goto _block;
}
++n;
lua_pushlstring(L, (const char *)buffer+2, size);
lua_rawseti(L, 3, n);
buffer += size + 2;
sz -= size + 2;
return unpack(L, buffer, sz, n);
_block:
lua_pushboolean(L, n==0 ? 0:1);
if (sz == 0) {
lua_pushnil(L);
} else {
lua_pushlstring(L, (const char *)buffer, sz);
}
return 2;
}
/*
intger fd
string last
@@ -125,46 +90,31 @@ _block:
boolean (true: data, false: block, nil: close)
string last
*/
struct socket_buffer {
void * buffer;
int sz;
};
static int
lrecv(lua_State *L) {
int fd = luaL_checkinteger(L,1);
size_t sz = 0;
const char * last = lua_tolstring(L,2,&sz);
luaL_checktype(L, 3, LUA_TTABLE);
char tmp[CACHE_SIZE];
char * buffer;
int r = recv(fd, tmp, CACHE_SIZE, 0);
char buffer[CACHE_SIZE];
int r = recv(fd, buffer, CACHE_SIZE, 0);
if (r == 0) {
lua_pushliteral(L, "");
// close
return 0;
return 1;
}
if (r < 0) {
if (errno == EAGAIN || errno == EINTR) {
lua_pushboolean(L, 0);
lua_pushvalue(L, 2);
return 2;
return 0;
}
luaL_error(L, "socket error: %s", strerror(errno));
}
if (sz + r <= CACHE_SIZE) {
buffer = tmp;
memmove(buffer + sz, buffer, r);
memcpy(buffer, last, sz);
} else {
buffer = lua_newuserdata(L, r + sz);
memcpy(buffer, last, sz);
memcpy(buffer + sz, tmp, r);
}
int i;
int n = lua_rawlen(L, 3);
for (i=1;i<=n;i++) {
lua_pushnil(L);
lua_rawseti(L, 3, i);
}
return unpack(L, (uint8_t *)buffer, r+sz, 0);
lua_pushlstring(L, buffer, r);
return 1;
}
static int
@@ -219,7 +169,7 @@ readline_stdin(void * arg) {
}
static int
lreadline(lua_State *L) {
lreadstdin(lua_State *L) {
struct queue *q = lua_touserdata(L, lua_upvalueindex(1));
LOCK(q);
if (q->head == q->tail) {
@@ -251,8 +201,8 @@ luaopen_clientsocket(lua_State *L) {
struct queue * q = lua_newuserdata(L, sizeof(*q));
memset(q, 0, sizeof(*q));
lua_pushcclosure(L, lreadline, 1);
lua_setfield(L, -2, "readline");
lua_pushcclosure(L, lreadstdin, 1);
lua_setfield(L, -2, "readstdin");
pthread_t pid ;
pthread_create(&pid, NULL, readline_stdin, q);

View File

@@ -15,7 +15,7 @@
uint32_t next_session
*/
#define TEMP_LENGTH 0x10002
#define TEMP_LENGTH 0x10007
static void
fill_uint32(uint8_t * buf, uint32_t n) {
@@ -146,6 +146,7 @@ lunpackrequest(lua_State *L) {
/*
int session
boolean ok
lightuserdata msg
int sz
return string response
@@ -155,15 +156,27 @@ lpackresponse(lua_State *L) {
uint32_t session = luaL_checkunsigned(L,1);
// clusterd.lua:command.socket call lpackresponse,
// and the msg/sz is return by skynet.rawcall , so don't free(msg)
void * msg = lua_touserdata(L,2);
size_t sz = luaL_checkunsigned(L, 3);
int ok = lua_toboolean(L,2);
void * msg;
size_t sz;
if (lua_type(L,3) == LUA_TSTRING) {
msg = (void *)lua_tolstring(L, 3, &sz);
if (sz > 0x1000) {
sz = 0x1000;
}
} else {
msg = lua_touserdata(L,3);
sz = luaL_checkunsigned(L, 4);
}
uint8_t buf[TEMP_LENGTH];
fill_header(L, buf, sz+4, msg);
fill_header(L, buf, sz+5, msg);
fill_uint32(buf+2, session);
memcpy(buf+6,msg,sz);
buf[6] = ok;
memcpy(buf+7,msg,sz);
lua_pushlstring(L, (const char *)buf, sz+6);
lua_pushlstring(L, (const char *)buf, sz+7);
return 1;
}
@@ -178,19 +191,19 @@ static int
lunpackresponse(lua_State *L) {
size_t sz;
const char * buf = luaL_checklstring(L, 1, &sz);
if (sz < 4) {
if (sz < 5) {
return 0;
}
uint32_t session = unpack_uint32((const uint8_t *)buf);
lua_pushunsigned(L, session);
lua_pushboolean(L, 1);
lua_pushlstring(L, buf+4, sz-4);
lua_pushboolean(L, buf[4]);
lua_pushlstring(L, buf+5, sz-5);
return 3;
}
int
luaopen_cluster_c(lua_State *L) {
luaopen_cluster_core(lua_State *L) {
luaL_Reg l[] = {
{ "packrequest", lpackrequest },
{ "unpackrequest", lunpackrequest },

859
lualib-src/lua-crypt.c Normal file
View File

@@ -0,0 +1,859 @@
#include <lua.h>
#include <lauxlib.h>
#include <time.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define SMALL_CHUNK 256
/* the eight DES S-boxes */
uint32_t SB1[64] = {
0x01010400, 0x00000000, 0x00010000, 0x01010404,
0x01010004, 0x00010404, 0x00000004, 0x00010000,
0x00000400, 0x01010400, 0x01010404, 0x00000400,
0x01000404, 0x01010004, 0x01000000, 0x00000004,
0x00000404, 0x01000400, 0x01000400, 0x00010400,
0x00010400, 0x01010000, 0x01010000, 0x01000404,
0x00010004, 0x01000004, 0x01000004, 0x00010004,
0x00000000, 0x00000404, 0x00010404, 0x01000000,
0x00010000, 0x01010404, 0x00000004, 0x01010000,
0x01010400, 0x01000000, 0x01000000, 0x00000400,
0x01010004, 0x00010000, 0x00010400, 0x01000004,
0x00000400, 0x00000004, 0x01000404, 0x00010404,
0x01010404, 0x00010004, 0x01010000, 0x01000404,
0x01000004, 0x00000404, 0x00010404, 0x01010400,
0x00000404, 0x01000400, 0x01000400, 0x00000000,
0x00010004, 0x00010400, 0x00000000, 0x01010004
};
static uint32_t SB2[64] = {
0x80108020, 0x80008000, 0x00008000, 0x00108020,
0x00100000, 0x00000020, 0x80100020, 0x80008020,
0x80000020, 0x80108020, 0x80108000, 0x80000000,
0x80008000, 0x00100000, 0x00000020, 0x80100020,
0x00108000, 0x00100020, 0x80008020, 0x00000000,
0x80000000, 0x00008000, 0x00108020, 0x80100000,
0x00100020, 0x80000020, 0x00000000, 0x00108000,
0x00008020, 0x80108000, 0x80100000, 0x00008020,
0x00000000, 0x00108020, 0x80100020, 0x00100000,
0x80008020, 0x80100000, 0x80108000, 0x00008000,
0x80100000, 0x80008000, 0x00000020, 0x80108020,
0x00108020, 0x00000020, 0x00008000, 0x80000000,
0x00008020, 0x80108000, 0x00100000, 0x80000020,
0x00100020, 0x80008020, 0x80000020, 0x00100020,
0x00108000, 0x00000000, 0x80008000, 0x00008020,
0x80000000, 0x80100020, 0x80108020, 0x00108000
};
static uint32_t SB3[64] = {
0x00000208, 0x08020200, 0x00000000, 0x08020008,
0x08000200, 0x00000000, 0x00020208, 0x08000200,
0x00020008, 0x08000008, 0x08000008, 0x00020000,
0x08020208, 0x00020008, 0x08020000, 0x00000208,
0x08000000, 0x00000008, 0x08020200, 0x00000200,
0x00020200, 0x08020000, 0x08020008, 0x00020208,
0x08000208, 0x00020200, 0x00020000, 0x08000208,
0x00000008, 0x08020208, 0x00000200, 0x08000000,
0x08020200, 0x08000000, 0x00020008, 0x00000208,
0x00020000, 0x08020200, 0x08000200, 0x00000000,
0x00000200, 0x00020008, 0x08020208, 0x08000200,
0x08000008, 0x00000200, 0x00000000, 0x08020008,
0x08000208, 0x00020000, 0x08000000, 0x08020208,
0x00000008, 0x00020208, 0x00020200, 0x08000008,
0x08020000, 0x08000208, 0x00000208, 0x08020000,
0x00020208, 0x00000008, 0x08020008, 0x00020200
};
static uint32_t SB4[64] = {
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802080, 0x00800081, 0x00800001, 0x00002001,
0x00000000, 0x00802000, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00800080, 0x00800001,
0x00000001, 0x00002000, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002001, 0x00002080,
0x00800081, 0x00000001, 0x00002080, 0x00800080,
0x00002000, 0x00802080, 0x00802081, 0x00000081,
0x00800080, 0x00800001, 0x00802000, 0x00802081,
0x00000081, 0x00000000, 0x00000000, 0x00802000,
0x00002080, 0x00800080, 0x00800081, 0x00000001,
0x00802001, 0x00002081, 0x00002081, 0x00000080,
0x00802081, 0x00000081, 0x00000001, 0x00002000,
0x00800001, 0x00002001, 0x00802080, 0x00800081,
0x00002001, 0x00002080, 0x00800000, 0x00802001,
0x00000080, 0x00800000, 0x00002000, 0x00802080
};
static uint32_t SB5[64] = {
0x00000100, 0x02080100, 0x02080000, 0x42000100,
0x00080000, 0x00000100, 0x40000000, 0x02080000,
0x40080100, 0x00080000, 0x02000100, 0x40080100,
0x42000100, 0x42080000, 0x00080100, 0x40000000,
0x02000000, 0x40080000, 0x40080000, 0x00000000,
0x40000100, 0x42080100, 0x42080100, 0x02000100,
0x42080000, 0x40000100, 0x00000000, 0x42000000,
0x02080100, 0x02000000, 0x42000000, 0x00080100,
0x00080000, 0x42000100, 0x00000100, 0x02000000,
0x40000000, 0x02080000, 0x42000100, 0x40080100,
0x02000100, 0x40000000, 0x42080000, 0x02080100,
0x40080100, 0x00000100, 0x02000000, 0x42080000,
0x42080100, 0x00080100, 0x42000000, 0x42080100,
0x02080000, 0x00000000, 0x40080000, 0x42000000,
0x00080100, 0x02000100, 0x40000100, 0x00080000,
0x00000000, 0x40080000, 0x02080100, 0x40000100
};
static uint32_t SB6[64] = {
0x20000010, 0x20400000, 0x00004000, 0x20404010,
0x20400000, 0x00000010, 0x20404010, 0x00400000,
0x20004000, 0x00404010, 0x00400000, 0x20000010,
0x00400010, 0x20004000, 0x20000000, 0x00004010,
0x00000000, 0x00400010, 0x20004010, 0x00004000,
0x00404000, 0x20004010, 0x00000010, 0x20400010,
0x20400010, 0x00000000, 0x00404010, 0x20404000,
0x00004010, 0x00404000, 0x20404000, 0x20000000,
0x20004000, 0x00000010, 0x20400010, 0x00404000,
0x20404010, 0x00400000, 0x00004010, 0x20000010,
0x00400000, 0x20004000, 0x20000000, 0x00004010,
0x20000010, 0x20404010, 0x00404000, 0x20400000,
0x00404010, 0x20404000, 0x00000000, 0x20400010,
0x00000010, 0x00004000, 0x20400000, 0x00404010,
0x00004000, 0x00400010, 0x20004010, 0x00000000,
0x20404000, 0x20000000, 0x00400010, 0x20004010
};
static uint32_t SB7[64] = {
0x00200000, 0x04200002, 0x04000802, 0x00000000,
0x00000800, 0x04000802, 0x00200802, 0x04200800,
0x04200802, 0x00200000, 0x00000000, 0x04000002,
0x00000002, 0x04000000, 0x04200002, 0x00000802,
0x04000800, 0x00200802, 0x00200002, 0x04000800,
0x04000002, 0x04200000, 0x04200800, 0x00200002,
0x04200000, 0x00000800, 0x00000802, 0x04200802,
0x00200800, 0x00000002, 0x04000000, 0x00200800,
0x04000000, 0x00200800, 0x00200000, 0x04000802,
0x04000802, 0x04200002, 0x04200002, 0x00000002,
0x00200002, 0x04000000, 0x04000800, 0x00200000,
0x04200800, 0x00000802, 0x00200802, 0x04200800,
0x00000802, 0x04000002, 0x04200802, 0x04200000,
0x00200800, 0x00000000, 0x00000002, 0x04200802,
0x00000000, 0x00200802, 0x04200000, 0x00000800,
0x04000002, 0x04000800, 0x00000800, 0x00200002
};
static uint32_t SB8[64] = {
0x10001040, 0x00001000, 0x00040000, 0x10041040,
0x10000000, 0x10001040, 0x00000040, 0x10000000,
0x00040040, 0x10040000, 0x10041040, 0x00041000,
0x10041000, 0x00041040, 0x00001000, 0x00000040,
0x10040000, 0x10000040, 0x10001000, 0x00001040,
0x00041000, 0x00040040, 0x10040040, 0x10041000,
0x00001040, 0x00000000, 0x00000000, 0x10040040,
0x10000040, 0x10001000, 0x00041040, 0x00040000,
0x00041040, 0x00040000, 0x10041000, 0x00001000,
0x00000040, 0x10040040, 0x00001000, 0x00041040,
0x10001000, 0x00000040, 0x10000040, 0x10040000,
0x10040040, 0x10000000, 0x00040000, 0x10001040,
0x00000000, 0x10041040, 0x00040040, 0x10000040,
0x10040000, 0x10001000, 0x10001040, 0x00000000,
0x10041040, 0x00041000, 0x00041000, 0x00001040,
0x00001040, 0x00040040, 0x10000000, 0x10041000
};
/* PC1: left and right halves bit-swap */
static uint32_t LHs[16] = {
0x00000000, 0x00000001, 0x00000100, 0x00000101,
0x00010000, 0x00010001, 0x00010100, 0x00010101,
0x01000000, 0x01000001, 0x01000100, 0x01000101,
0x01010000, 0x01010001, 0x01010100, 0x01010101
};
static uint32_t RHs[16] = {
0x00000000, 0x01000000, 0x00010000, 0x01010000,
0x00000100, 0x01000100, 0x00010100, 0x01010100,
0x00000001, 0x01000001, 0x00010001, 0x01010001,
0x00000101, 0x01000101, 0x00010101, 0x01010101,
};
/* platform-independant 32-bit integer manipulation macros */
#define GET_UINT32(n,b,i) \
{ \
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
| ( (uint32_t) (b)[(i) + 3] ); \
}
#define PUT_UINT32(n,b,i) \
{ \
(b)[(i) ] = (uint8_t) ( (n) >> 24 ); \
(b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \
(b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \
(b)[(i) + 3] = (uint8_t) ( (n) ); \
}
/* Initial Permutation macro */
#define DES_IP(X,Y) \
{ \
T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \
T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \
X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \
}
/* Final Permutation macro */
#define DES_FP(X,Y) \
{ \
X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \
T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \
Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \
T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
}
/* DES round macro */
#define DES_ROUND(X,Y) \
{ \
T = *SK++ ^ X; \
Y ^= SB8[ (T ) & 0x3F ] ^ \
SB6[ (T >> 8) & 0x3F ] ^ \
SB4[ (T >> 16) & 0x3F ] ^ \
SB2[ (T >> 24) & 0x3F ]; \
\
T = *SK++ ^ ((X << 28) | (X >> 4)); \
Y ^= SB7[ (T ) & 0x3F ] ^ \
SB5[ (T >> 8) & 0x3F ] ^ \
SB3[ (T >> 16) & 0x3F ] ^ \
SB1[ (T >> 24) & 0x3F ]; \
}
/* DES key schedule */
static void
des_main_ks( uint32_t SK[32], const uint8_t key[8] ) {
int i;
uint32_t X, Y, T;
GET_UINT32( X, key, 0 );
GET_UINT32( Y, key, 4 );
/* Permuted Choice 1 */
T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4);
T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T );
X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2)
| (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] )
| (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6)
| (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4);
Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2)
| (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] )
| (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6)
| (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4);
X &= 0x0FFFFFFF;
Y &= 0x0FFFFFFF;
/* calculate subkeys */
for( i = 0; i < 16; i++ )
{
if( i < 2 || i == 8 || i == 15 )
{
X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF;
Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF;
}
else
{
X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF;
Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF;
}
*SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000)
| ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
| ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000)
| ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000)
| ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000)
| ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000)
| ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400)
| ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100)
| ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010)
| ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004)
| ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);
*SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
| ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
| ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000)
| ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
| ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000)
| ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000)
| ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000)
| ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400)
| ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100)
| ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011)
| ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002);
}
}
/* DES 64-bit block encryption/decryption */
static void
des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) {
uint32_t X, Y, T;
GET_UINT32( X, input, 0 );
GET_UINT32( Y, input, 4 );
DES_IP( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_ROUND( Y, X ); DES_ROUND( X, Y );
DES_FP( Y, X );
PUT_UINT32( Y, output, 0 );
PUT_UINT32( X, output, 4 );
}
static int
lrandomkey(lua_State *L) {
char tmp[8];
int i;
for (i=0;i<8;i++) {
tmp[i] = random() & 0xff;
}
lua_pushlstring(L, tmp, 8);
return 1;
}
static void
des_key(lua_State *L, uint32_t SK[32]) {
size_t keysz = 0;
const void * key = luaL_checklstring(L, 1, &keysz);
if (keysz != 8) {
luaL_error(L, "Invalid key size %d, need 8 bytes", (int)keysz);
}
des_main_ks(SK, key);
}
static int
ldesencode(lua_State *L) {
uint32_t SK[32];
des_key(L, SK);
size_t textsz = 0;
const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &textsz);
size_t chunksz = (textsz + 8) & ~7;
uint8_t tmp[SMALL_CHUNK];
uint8_t *buffer = tmp;
if (chunksz > SMALL_CHUNK) {
buffer = lua_newuserdata(L, chunksz);
}
int i;
for (i=0;i<(int)textsz-7;i+=8) {
des_crypt(SK, text+i, buffer+i);
}
int bytes = textsz - i;
uint8_t tail[8];
int j;
for (j=0;j<8;j++) {
if (j < bytes) {
tail[j] = text[i+j];
} else if (j==bytes) {
tail[j] = 0x80;
} else {
tail[j] = 0;
}
}
des_crypt(SK, tail, buffer+i);
lua_pushlstring(L, (const char *)buffer, chunksz);
return 1;
}
static int
ldesdecode(lua_State *L) {
uint32_t ESK[32];
des_key(L, ESK);
uint32_t SK[32];
int i;
for( i = 0; i < 32; i += 2 ) {
SK[i] = ESK[30 - i];
SK[i + 1] = ESK[31 - i];
}
size_t textsz = 0;
const uint8_t *text = (const uint8_t *)luaL_checklstring(L, 2, &textsz);
if ((textsz & 7) || textsz == 0) {
return luaL_error(L, "Invalid des crypt text length %d", (int)textsz);
}
uint8_t tmp[SMALL_CHUNK];
uint8_t *buffer = tmp;
if (textsz > SMALL_CHUNK) {
buffer = lua_newuserdata(L, textsz);
}
for (i=0;i<textsz;i+=8) {
des_crypt(SK, text+i, buffer+i);
}
int padding = 1;
for (i=textsz-1;i>=textsz-8;i--) {
if (buffer[i] == 0) {
padding++;
} else if (buffer[i] == 0x80) {
break;
} else {
return luaL_error(L, "Invalid des crypt text");
}
}
if (padding > 8) {
return luaL_error(L, "Invalid des crypt text");
}
lua_pushlstring(L, (const char *)buffer, textsz - padding);
return 1;
}
static void
Hash(const char * str, int sz, uint8_t key[8]) {
uint32_t djb_hash = 5381L;
uint32_t js_hash = 1315423911L;
int i;
for (i=0;i<sz;i++) {
uint8_t c = (uint8_t)str[i];
djb_hash += (djb_hash << 5) + c;
js_hash ^= ((js_hash << 5) + c + (js_hash >> 2));
}
key[0] = djb_hash & 0xff;
key[1] = (djb_hash >> 8) & 0xff;
key[2] = (djb_hash >> 16) & 0xff;
key[3] = (djb_hash >> 24) & 0xff;
key[4] = js_hash & 0xff;
key[5] = (js_hash >> 8) & 0xff;
key[6] = (js_hash >> 16) & 0xff;
key[7] = (js_hash >> 24) & 0xff;
}
static int
lhashkey(lua_State *L) {
size_t sz = 0;
const char * key = luaL_checklstring(L, 1, &sz);
uint8_t realkey[8];
Hash(key,(int)sz,realkey);
lua_pushlstring(L, (const char *)realkey, 8);
return 1;
}
static int
ltohex(lua_State *L) {
static char hex[] = "0123456789abcdef";
size_t sz = 0;
const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz);
char tmp[SMALL_CHUNK];
char *buffer = tmp;
if (sz > SMALL_CHUNK/2) {
buffer = lua_newuserdata(L, sz * 2);
}
int i;
for (i=0;i<sz;i++) {
buffer[i*2] = hex[text[i] >> 4];
buffer[i*2+1] = hex[text[i] & 0xf];
}
lua_pushlstring(L, buffer, sz * 2);
return 1;
}
#define HEX(v,c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } }
static int
lfromhex(lua_State *L) {
size_t sz = 0;
const char * text = luaL_checklstring(L, 1, &sz);
if (sz & 2) {
return luaL_error(L, "Invalid hex text size %d", (int)sz);
}
char tmp[SMALL_CHUNK];
char *buffer = tmp;
if (sz > SMALL_CHUNK*2) {
buffer = lua_newuserdata(L, sz / 2);
}
int i;
for (i=0;i<sz;i+=2) {
uint8_t hi,low;
HEX(hi, text[i]);
HEX(low, text[i+1]);
if (hi > 16 || low > 16) {
return luaL_error(L, "Invalid hex text", text);
}
buffer[i/2] = hi<<4 | low;
}
lua_pushlstring(L, buffer, i/2);
return 1;
}
// Constants are the integer part of the sines of integers (in radians) * 2^32.
const uint32_t k[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee ,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 ,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be ,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 ,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa ,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 ,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed ,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a ,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c ,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 ,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 ,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 ,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 ,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 ,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 ,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };
// r specifies the per-round shift amounts
const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
// leftrotate function definition
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
static void
hmac(uint32_t x[2], uint32_t y[2], uint32_t result[2]) {
uint32_t w[16];
uint32_t a, b, c, d, f, g, temp;
int i;
a = 0x67452301u;
b = 0xefcdab89u;
c = 0x98badcfeu;
d = 0x10325476u;
for (i=0;i<16;i+=4) {
w[i] = x[1];
w[i+1] = x[0];
w[i+2] = y[1];
w[i+3] = y[0];
}
for(i = 0; i<64; i++) {
if (i < 16) {
f = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
f = (d & b) | ((~d) & c);
g = (5*i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3*i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7*i) % 16;
}
temp = d;
d = c;
c = b;
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]);
a = temp;
}
result[0] = c^d;
result[1] = a^b;
}
static void
read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) {
size_t sz = 0;
const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz);
if (sz != 8) {
luaL_error(L, "Invalid uint64 x");
}
const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz);
if (sz != 8) {
luaL_error(L, "Invalid uint64 y");
}
xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24;
xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24;
yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24;
yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24;
}
static int
lhmac64(lua_State *L) {
uint32_t x[2], y[2];
read64(L, x, y);
uint32_t result[2];
hmac(x,y,result);
uint8_t tmp[8];
tmp[0] = result[0] & 0xff;
tmp[1] = (result[0] >> 8 )& 0xff;
tmp[2] = (result[0] >> 16 )& 0xff;
tmp[3] = (result[0] >> 24 )& 0xff;
tmp[4] = result[1] & 0xff;
tmp[5] = (result[1] >> 8 )& 0xff;
tmp[6] = (result[1] >> 16 )& 0xff;
tmp[7] = (result[1] >> 24 )& 0xff;
lua_pushlstring(L, (const char *)tmp, 8);
return 1;
}
// powmodp64 for DH-key exchange
// The biggest 64bit prime
#define P 0xffffffffffffffc5ull
static inline uint64_t
mul_mod_p(uint64_t a, uint64_t b) {
uint64_t m = 0;
while(b) {
if(b&1) {
uint64_t t = P-a;
if ( m >= t) {
m -= t;
} else {
m += a;
}
}
if (a >= P - a) {
a = a * 2 - P;
} else {
a = a * 2;
}
b>>=1;
}
return m;
}
static inline uint64_t
pow_mod_p(uint64_t a, uint64_t b) {
if (b==1) {
return a;
}
uint64_t t = pow_mod_p(a, b>>1);
t = mul_mod_p(t,t);
if (b % 2) {
t = mul_mod_p(t, a);
}
return t;
}
// calc a^b % p
static uint64_t
powmodp(uint64_t a, uint64_t b) {
if (a > P)
a%=P;
return pow_mod_p(a,b);
}
static void
push64(lua_State *L, uint64_t r) {
uint8_t tmp[8];
tmp[0] = r & 0xff;
tmp[1] = (r >> 8 )& 0xff;
tmp[2] = (r >> 16 )& 0xff;
tmp[3] = (r >> 24 )& 0xff;
tmp[4] = (r >> 32 )& 0xff;
tmp[5] = (r >> 40 )& 0xff;
tmp[6] = (r >> 48 )& 0xff;
tmp[7] = (r >> 56 )& 0xff;
lua_pushlstring(L, (const char *)tmp, 8);
}
static int
ldhsecret(lua_State *L) {
uint32_t x[2], y[2];
read64(L, x, y);
uint64_t r = powmodp((uint64_t)x[0] | (uint64_t)x[1]<<32,
(uint64_t)y[0] | (uint64_t)y[1]<<32);
push64(L, r);
return 1;
}
#define G 5
static int
ldhexchange(lua_State *L) {
size_t sz = 0;
const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz);
if (sz != 8) {
luaL_error(L, "Invalid dh uint64 key");
}
uint32_t xx[2];
xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24;
xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24;
uint64_t r = powmodp(5, (uint64_t)xx[0] | (uint64_t)xx[1]<<32);
push64(L, r);
return 1;
}
// base64
static int
lb64encode(lua_State *L) {
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t sz = 0;
const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz);
int encode_sz = (sz + 2)/3*4;
char tmp[SMALL_CHUNK];
char *buffer = tmp;
if (encode_sz > SMALL_CHUNK) {
buffer = lua_newuserdata(L, encode_sz);
}
int i,j;
j=0;
for (i=0;i<(int)sz-2;i+=3) {
uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2];
buffer[j] = encoding[v >> 18];
buffer[j+1] = encoding[(v >> 12) & 0x3f];
buffer[j+2] = encoding[(v >> 6) & 0x3f];
buffer[j+3] = encoding[(v) & 0x3f];
j+=4;
}
int padding = sz-i;
uint32_t v;
switch(padding) {
case 1 :
v = text[i];
buffer[j] = encoding[v >> 2];
buffer[j+1] = encoding[(v & 3) << 4];
buffer[j+2] = '=';
buffer[j+3] = '=';
break;
case 2 :
v = text[i] << 8 | text[i+1];
buffer[j] = encoding[v >> 10];
buffer[j+1] = encoding[(v >> 4) & 0x3f];
buffer[j+2] = encoding[(v & 0xf) << 2];
buffer[j+3] = '=';
break;
}
lua_pushlstring(L, buffer, encode_sz);
return 1;
}
static inline int
b64index(uint8_t c) {
static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
int decoding_size = sizeof(decoding)/sizeof(decoding[0]);
if (c<43) {
return -1;
}
c -= 43;
if (c>=decoding_size)
return -1;
return decoding[c];
}
static int
lb64decode(lua_State *L) {
size_t sz = 0;
const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz);
int decode_sz = (sz+3)/4*3;
char tmp[SMALL_CHUNK];
char *buffer = tmp;
if (decode_sz > SMALL_CHUNK) {
buffer = lua_newuserdata(L, decode_sz);
}
int i,j;
int output = 0;
for (i=0;i<sz;) {
int padding = 0;
int c[4];
for (j=0;j<4;) {
if (i>=sz) {
return luaL_error(L, "Invalid base64 text");
}
c[j] = b64index(text[i]);
if (c[j] == -1) {
++i;
continue;
}
if (c[j] == -2) {
++padding;
}
++i;
++j;
}
uint32_t v;
switch (padding) {
case 0:
v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3];
buffer[output] = v >> 16;
buffer[output+1] = (v >> 8) & 0xff;
buffer[output+2] = v & 0xff;
output += 3;
break;
case 1:
if (c[3] != -2 || (c[2] & 3)!=0) {
return luaL_error(L, "Invalid base64 text");
}
v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ;
buffer[output] = v >> 8;
buffer[output+1] = v & 0xff;
output += 2;
break;
case 2:
if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) {
return luaL_error(L, "Invalid base64 text");
}
v = (unsigned)c[0] << 2 | c[1] >> 4;
buffer[output] = v;
++ output;
break;
default:
return luaL_error(L, "Invalid base64 text");
}
}
lua_pushlstring(L, buffer, output);
return 1;
}
int
luaopen_crypt(lua_State *L) {
luaL_checkversion(L);
srandom(time(NULL));
luaL_Reg l[] = {
{ "hashkey", lhashkey },
{ "randomkey", lrandomkey },
{ "desencode", ldesencode },
{ "desdecode", ldesdecode },
{ "hexencode", ltohex },
{ "hexdecode", lfromhex },
{ "hmac64", lhmac64 },
{ "dhexchange", ldhexchange },
{ "dhsecret", ldhsecret },
{ "base64encode", lb64encode },
{ "base64decode", lb64decode },
{ NULL, NULL },
};
luaL_newlib(L,l);
return 1;
}

View File

@@ -506,9 +506,9 @@ op_insert(lua_State *L) {
int i;
for (i=1;i<=s;i++) {
lua_rawgeti(L,3,i);
document doc = lua_touserdata(L,3);
document doc = lua_touserdata(L,-1);
lua_pop(L,1); // must call lua_pop before luaL_addlstring, because addlstring may change stack top
luaL_addlstring(&b, (const char *)doc, get_length(doc));
lua_pop(L,1);
}
}

View File

@@ -149,7 +149,7 @@ mc_nextid(lua_State *L) {
}
int
luaopen_multicast_c(lua_State *L) {
luaopen_multicast_core(lua_State *L) {
luaL_Reg l[] = {
{ "pack", mc_packlocal },
{ "unpack", mc_unpacklocal },

View File

@@ -393,13 +393,13 @@ lpop(lua_State *L) {
*/
static const char *
tolstring(lua_State *L, size_t *sz) {
tolstring(lua_State *L, size_t *sz, int index) {
const char * ptr;
if (lua_isuserdata(L,1)) {
ptr = (const char *)lua_touserdata(L,1);
*sz = (size_t)luaL_checkinteger(L, 2);
if (lua_isuserdata(L,index)) {
ptr = (const char *)lua_touserdata(L,index);
*sz = (size_t)luaL_checkinteger(L, index+1);
} else {
ptr = luaL_checklstring(L, 1, sz);
ptr = luaL_checklstring(L, index, sz);
}
return ptr;
}
@@ -413,7 +413,7 @@ write_size(uint8_t * buffer, int len) {
static int
lpack(lua_State *L) {
size_t len;
const char * ptr = tolstring(L, &len);
const char * ptr = tolstring(L, &len, 1);
if (len > 0x10000) {
return luaL_error(L, "Invalid size (too long) of data : %d", (int)len);
}
@@ -433,7 +433,7 @@ lpack_string(lua_State *L) {
uint8_t tmp[SMALLSTRING+2];
size_t len;
uint8_t *buffer;
const char * ptr = tolstring(L, &len);
const char * ptr = tolstring(L, &len, 1);
if (len > 0x10000) {
return luaL_error(L, "Invalid size (too long) of data : %d", (int)len);
}
@@ -451,6 +451,34 @@ lpack_string(lua_State *L) {
return 1;
}
static int
lpack_padding(lua_State *L) {
uint8_t tmp[SMALLSTRING+2];
size_t content_sz;
uint8_t *buffer;
const char * ptr = tolstring(L, &content_sz, 2);
size_t cookie_sz = 0;
const char * cookie = luaL_checklstring(L,1,&cookie_sz);
size_t len = cookie_sz + content_sz;
if (len > 0x10000) {
return luaL_error(L, "Invalid size (too long) of data : %d", (int)len);
}
if (len <= SMALLSTRING) {
buffer = tmp;
} else {
buffer = lua_newuserdata(L, len + 2);
}
write_size(buffer, len);
memcpy(buffer+2, ptr, content_sz);
memcpy(buffer+2+content_sz, cookie, cookie_sz);
lua_pushlstring(L, (const char *)buffer, len+2);
return 1;
}
static int
ltostring(lua_State *L) {
void * ptr = lua_touserdata(L, 1);
@@ -458,8 +486,19 @@ ltostring(lua_State *L) {
if (ptr == NULL) {
lua_pushliteral(L, "");
} else {
lua_pushlstring(L, (const char *)ptr, size);
skynet_free(ptr);
if (lua_isnumber(L, 3)) {
int offset = lua_tointeger(L, 3);
if (offset < 0) {
return luaL_error(L, "Invalid offset %d", offset);
}
if (offset > size) {
offset = size;
}
lua_pushlstring(L, (const char *)ptr + offset, size-offset);
} else {
lua_pushlstring(L, (const char *)ptr, size);
skynet_free(ptr);
}
}
return 1;
}
@@ -471,6 +510,7 @@ luaopen_netpack(lua_State *L) {
{ "pop", lpop },
{ "pack", lpack },
{ "pack_string", lpack_string },
{ "pack_padding", lpack_padding },
{ "clear", lclear },
{ "tostring", ltostring },
{ NULL, NULL },

View File

@@ -1,5 +1,5 @@
/*
https://github.com/cloudwu/lua-serialize
modify from https://github.com/cloudwu/lua-serialize
*/
#include "skynet_malloc.h"
@@ -35,14 +35,13 @@ struct block {
struct write_block {
struct block * head;
int len;
struct block * current;
int len;
int ptr;
};
struct read_block {
char * buffer;
struct block * current;
int len;
int ptr;
};
@@ -78,38 +77,17 @@ _again:
static void
wb_init(struct write_block *wb , struct block *b) {
if (b==NULL) {
wb->head = blk_alloc();
wb->len = 0;
wb->current = wb->head;
wb->ptr = 0;
wb_push(wb, &wb->len, sizeof(wb->len));
} else {
wb->head = b;
int * plen = (int *)b->buffer;
int sz = *plen;
wb->len = sz;
while (b->next) {
sz -= BLOCK_SIZE;
b = b->next;
}
wb->current = b;
wb->ptr = sz;
}
}
static struct block *
wb_close(struct write_block *b) {
b->current = b->head;
b->ptr = 0;
wb_push(b, &b->len, sizeof(b->len));
b->current = NULL;
return b->head;
wb->head = b;
assert(b->next == NULL);
wb->len = 0;
wb->current = wb->head;
wb->ptr = 0;
}
static void
wb_free(struct write_block *wb) {
struct block *blk = wb->head;
blk = blk->next; // the first block is on stack
while (blk) {
struct block * next = blk->next;
skynet_free(blk);
@@ -124,7 +102,6 @@ wb_free(struct write_block *wb) {
static void
rball_init(struct read_block * rb, char * buffer, int size) {
rb->buffer = buffer;
rb->current = NULL;
rb->len = size;
rb->ptr = 0;
}
@@ -135,63 +112,10 @@ rb_read(struct read_block *rb, void *buffer, int sz) {
return NULL;
}
if (rb->buffer) {
int ptr = rb->ptr;
rb->ptr += sz;
rb->len -= sz;
return rb->buffer + ptr;
}
if (rb->ptr == BLOCK_SIZE) {
struct block * next = rb->current->next;
skynet_free(rb->current);
rb->current = next;
rb->ptr = 0;
}
int copy = BLOCK_SIZE - rb->ptr;
if (sz <= copy) {
void * ret = rb->current->buffer + rb->ptr;
rb->ptr += sz;
rb->len -= sz;
return ret;
}
char * tmp = buffer;
memcpy(tmp, rb->current->buffer + rb->ptr, copy);
sz -= copy;
tmp += copy;
rb->len -= copy;
for (;;) {
struct block * next = rb->current->next;
skynet_free(rb->current);
rb->current = next;
if (sz < BLOCK_SIZE) {
memcpy(tmp, rb->current->buffer, sz);
rb->ptr = sz;
rb->len -= sz;
return buffer;
}
memcpy(tmp, rb->current->buffer, BLOCK_SIZE);
sz -= BLOCK_SIZE;
tmp += BLOCK_SIZE;
rb->len -= BLOCK_SIZE;
}
}
static void
rb_close(struct read_block *rb) {
while (rb->current) {
struct block * next = rb->current->next;
skynet_free(rb->current);
rb->current = next;
}
rb->len = 0;
rb->ptr = 0;
int ptr = rb->ptr;
rb->ptr += sz;
rb->len -= sz;
return rb->buffer + ptr;
}
static inline void
@@ -271,7 +195,7 @@ wb_string(struct write_block *wb, const char *str, int len) {
}
}
static void _pack_one(lua_State *L, struct write_block *b, int index, int depth);
static void pack_one(lua_State *L, struct write_block *b, int index, int depth);
static int
wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) {
@@ -288,7 +212,7 @@ wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) {
int i;
for (i=1;i<=array_size;i++) {
lua_rawgeti(L,index,i);
_pack_one(L, wb, -1, depth);
pack_one(L, wb, -1, depth);
lua_pop(L,1);
}
@@ -307,8 +231,8 @@ wb_table_hash(lua_State *L, struct write_block * wb, int index, int depth, int a
continue;
}
}
_pack_one(L,wb,-2,depth);
_pack_one(L,wb,-1,depth);
pack_one(L,wb,-2,depth);
pack_one(L,wb,-1,depth);
lua_pop(L, 1);
}
wb_nil(wb);
@@ -324,7 +248,7 @@ wb_table(lua_State *L, struct write_block *wb, int index, int depth) {
}
static void
_pack_one(lua_State *L, struct write_block *b, int index, int depth) {
pack_one(lua_State *L, struct write_block *b, int index, int depth) {
if (depth > MAX_DEPTH) {
wb_free(b);
luaL_error(L, "serialize can't pack too depth table");
@@ -370,27 +294,24 @@ _pack_one(lua_State *L, struct write_block *b, int index, int depth) {
}
static void
_pack_from(lua_State *L, struct write_block *b, int from) {
pack_from(lua_State *L, struct write_block *b, int from) {
int n = lua_gettop(L) - from;
int i;
for (i=1;i<=n;i++) {
_pack_one(L, b , from + i, 0);
pack_one(L, b , from + i, 0);
}
}
static inline void
__invalid_stream(lua_State *L, struct read_block *rb, int line) {
invalid_stream_line(lua_State *L, struct read_block *rb, int line) {
int len = rb->len;
if (rb->buffer == NULL) {
rb_close(rb);
}
luaL_error(L, "Invalid serialize stream %d (line:%d)", len, line);
}
#define _invalid_stream(L,rb) __invalid_stream(L,rb,__LINE__)
#define invalid_stream(L,rb) invalid_stream_line(L,rb,__LINE__)
static int
_get_integer(lua_State *L, struct read_block *rb, int cookie) {
get_integer(lua_State *L, struct read_block *rb, int cookie) {
switch (cookie) {
case 0:
return 0;
@@ -398,93 +319,93 @@ _get_integer(lua_State *L, struct read_block *rb, int cookie) {
uint8_t n = 0;
uint8_t * pn = rb_read(rb,&n,1);
if (pn == NULL)
_invalid_stream(L,rb);
invalid_stream(L,rb);
return *pn;
}
case 2: {
uint16_t n = 0;
uint16_t * pn = rb_read(rb,&n,2);
if (pn == NULL)
_invalid_stream(L,rb);
invalid_stream(L,rb);
return *pn;
}
case 4: {
int n = 0;
int * pn = rb_read(rb,&n,4);
if (pn == NULL)
_invalid_stream(L,rb);
invalid_stream(L,rb);
return *pn;
}
default:
_invalid_stream(L,rb);
invalid_stream(L,rb);
return 0;
}
}
static double
_get_number(lua_State *L, struct read_block *rb, int cookie) {
get_number(lua_State *L, struct read_block *rb, int cookie) {
if (cookie == 8) {
double n = 0;
double * pn = rb_read(rb,&n,8);
if (pn == NULL)
_invalid_stream(L,rb);
invalid_stream(L,rb);
return *pn;
} else {
return (double)_get_integer(L,rb,cookie);
return (double)get_integer(L,rb,cookie);
}
}
static void *
_get_pointer(lua_State *L, struct read_block *rb) {
get_pointer(lua_State *L, struct read_block *rb) {
void * userdata = 0;
void ** v = (void **)rb_read(rb,&userdata,sizeof(userdata));
if (v == NULL) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
return *v;
}
static void
_get_buffer(lua_State *L, struct read_block *rb, int len) {
get_buffer(lua_State *L, struct read_block *rb, int len) {
char tmp[len];
char * p = rb_read(rb,tmp,len);
if (p == NULL) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
lua_pushlstring(L,p,len);
}
static void _unpack_one(lua_State *L, struct read_block *rb);
static void unpack_one(lua_State *L, struct read_block *rb);
static void
_unpack_table(lua_State *L, struct read_block *rb, int array_size) {
unpack_table(lua_State *L, struct read_block *rb, int array_size) {
if (array_size == MAX_COOKIE-1) {
uint8_t type = 0;
uint8_t *t = rb_read(rb, &type, 1);
if (t==NULL || (*t & 7) != TYPE_NUMBER) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
array_size = (int)_get_number(L,rb,*t >> 3);
array_size = (int)get_number(L,rb,*t >> 3);
}
lua_createtable(L,array_size,0);
int i;
for (i=1;i<=array_size;i++) {
_unpack_one(L,rb);
unpack_one(L,rb);
lua_rawseti(L,-2,i);
}
for (;;) {
_unpack_one(L,rb);
unpack_one(L,rb);
if (lua_isnil(L,-1)) {
lua_pop(L,1);
return;
}
_unpack_one(L,rb);
unpack_one(L,rb);
lua_rawset(L,-3);
}
}
static void
_push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
switch(type) {
case TYPE_NIL:
lua_pushnil(L);
@@ -493,82 +414,69 @@ _push_value(lua_State *L, struct read_block *rb, int type, int cookie) {
lua_pushboolean(L,cookie);
break;
case TYPE_NUMBER:
lua_pushnumber(L,_get_number(L,rb,cookie));
lua_pushnumber(L,get_number(L,rb,cookie));
break;
case TYPE_USERDATA:
lua_pushlightuserdata(L,_get_pointer(L,rb));
lua_pushlightuserdata(L,get_pointer(L,rb));
break;
case TYPE_SHORT_STRING:
_get_buffer(L,rb,cookie);
get_buffer(L,rb,cookie);
break;
case TYPE_LONG_STRING: {
uint32_t len;
if (cookie == 2) {
uint16_t *plen = rb_read(rb, &len, 2);
if (plen == NULL) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
_get_buffer(L,rb,(int)*plen);
get_buffer(L,rb,(int)*plen);
} else {
if (cookie != 4) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
uint32_t *plen = rb_read(rb, &len, 4);
if (plen == NULL) {
_invalid_stream(L,rb);
invalid_stream(L,rb);
}
_get_buffer(L,rb,(int)*plen);
get_buffer(L,rb,(int)*plen);
}
break;
}
case TYPE_TABLE: {
_unpack_table(L,rb,cookie);
unpack_table(L,rb,cookie);
break;
}
default: {
_invalid_stream(L,rb);
invalid_stream(L,rb);
break;
}
}
}
static void
_unpack_one(lua_State *L, struct read_block *rb) {
unpack_one(lua_State *L, struct read_block *rb) {
uint8_t type = 0;
uint8_t *t = rb_read(rb, &type, 1);
if (t==NULL) {
_invalid_stream(L, rb);
invalid_stream(L, rb);
}
_push_value(L, rb, *t & 0x7, *t>>3);
push_value(L, rb, *t & 0x7, *t>>3);
}
static void
_seri(lua_State *L, struct block *b) {
uint32_t len = 0;
memcpy(&len, b->buffer ,sizeof(len));
len -= 4;
seri(lua_State *L, struct block *b, int len) {
uint8_t * buffer = skynet_malloc(len);
uint8_t * ptr = buffer;
int sz = len;
if (len < BLOCK_SIZE - 4) {
memcpy(ptr, b->buffer+4, len);
} else {
memcpy(ptr, b->buffer+4, BLOCK_SIZE-4);
ptr += BLOCK_SIZE-4;
len -= BLOCK_SIZE-4;
b = b->next;
while(len>0) {
if (len >= BLOCK_SIZE) {
memcpy(ptr, b->buffer, BLOCK_SIZE);
ptr += BLOCK_SIZE;
len -= BLOCK_SIZE;
} else {
memcpy(ptr, b->buffer, len);
break;
}
while(len>0) {
if (len >= BLOCK_SIZE) {
memcpy(ptr, b->buffer, BLOCK_SIZE);
ptr += BLOCK_SIZE;
len -= BLOCK_SIZE;
b = b->next;
} else {
memcpy(ptr, b->buffer, len);
break;
}
}
@@ -581,8 +489,16 @@ _luaseri_unpack(lua_State *L) {
if (lua_isnoneornil(L,1)) {
return 0;
}
void * buffer = lua_touserdata(L,1);
int len = luaL_checkinteger(L,2);
void * buffer;
int len;
if (lua_type(L,1) == LUA_TSTRING) {
size_t sz;
buffer = (void *)lua_tolstring(L,1,&sz);
len = (int)sz;
} else {
buffer = lua_touserdata(L,1);
len = luaL_checkinteger(L,2);
}
if (len == 0) {
return 0;
}
@@ -603,7 +519,7 @@ _luaseri_unpack(lua_State *L) {
uint8_t *t = rb_read(&rb, &type, 1);
if (t==NULL)
break;
_push_value(L, &rb, *t & 0x7, *t>>3);
push_value(L, &rb, *t & 0x7, *t>>3);
}
// Need not free buffer
@@ -613,17 +529,15 @@ _luaseri_unpack(lua_State *L) {
int
_luaseri_pack(lua_State *L) {
struct block temp;
temp.next = NULL;
struct write_block wb;
wb_init(&wb, NULL);
_pack_from(L,&wb,0);
struct block * b = wb_close(&wb);
_seri(L,b);
wb_init(&wb, &temp);
pack_from(L,&wb,0);
assert(wb.head == &temp);
seri(L, &temp, wb.len);
while (b) {
struct block * next = b->next;
skynet_free(b);
b = next;
}
wb_free(&wb);
return 2;
}

789
lualib-src/lua-sharedata.c Normal file
View File

@@ -0,0 +1,789 @@
// build: gcc -O2 -Wall --shared -o conf.so luaconf.c
#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define KEYTYPE_INTEGER 0
#define KEYTYPE_STRING 1
#define VALUETYPE_NIL 0
#define VALUETYPE_NUMBER 1
#define VALUETYPE_STRING 2
#define VALUETYPE_BOOLEAN 3
#define VALUETYPE_TABLE 4
struct table;
union value {
lua_Number n;
struct table * tbl;
int string;
int boolean;
};
struct node {
union value v;
int key; // integer key or index of string table
int next; // next slot index
uint32_t keyhash;
uint8_t keytype; // key type must be integer or string
uint8_t valuetype; // value type can be number/string/boolean/table
uint8_t nocolliding; // 0 means colliding slot
};
struct table;
struct state {
int dirty;
int ref;
struct table * root;
};
struct table {
int sizearray;
int sizehash;
uint8_t *arraytype;
union value * array;
struct node * hash;
lua_State * L;
};
struct context {
lua_State * L;
struct table * tbl;
int string_index;
};
struct ctrl {
struct table * root;
struct table * update;
};
static int
countsize(lua_State *L, int sizearray) {
int n = 0;
lua_pushnil(L);
while (lua_next(L, 1) != 0) {
int type = lua_type(L, -2);
++n;
if (type == LUA_TNUMBER) {
lua_Number key = lua_tonumber(L, -2);
int nkey = (int)key;
if ((lua_Number)nkey != key) {
luaL_error(L, "Invalid key %f", key);
}
if (nkey > 0 && nkey <= sizearray) {
--n;
}
} else if (type != LUA_TSTRING && type != LUA_TTABLE) {
luaL_error(L, "Invalid key type %s", lua_typename(L, type));
}
lua_pop(L, 1);
}
return n;
}
static uint32_t
calchash(const char * str, size_t l) {
uint32_t h = (uint32_t)l;
size_t l1;
size_t step = (l >> 5) + 1;
for (l1 = l; l1 >= step; l1 -= step) {
h = h ^ ((h<<5) + (h>>2) + (uint8_t)(str[l1 - 1]));
}
return h;
}
static int
stringindex(struct context *ctx, const char * str, size_t sz) {
lua_State *L = ctx->L;
lua_pushlstring(L, str, sz);
lua_pushvalue(L, -1);
lua_rawget(L, 1);
int index;
// stringmap(1) str index
if (lua_isnil(L, -1)) {
index = ++ctx->string_index;
lua_pop(L, 1);
lua_pushinteger(L, index);
lua_rawset(L, 1);
} else {
index = lua_tointeger(L, -1);
lua_pop(L, 2);
}
return index;
}
static int convtable(lua_State *L);
static void
setvalue(struct context * ctx, lua_State *L, int index, struct node *n) {
int vt = lua_type(L, index);
switch(vt) {
case LUA_TNIL:
n->valuetype = VALUETYPE_NIL;
break;
case LUA_TNUMBER:
n->v.n = lua_tonumber(L, index);
n->valuetype = VALUETYPE_NUMBER;
break;
case LUA_TSTRING: {
size_t sz = 0;
const char * str = lua_tolstring(L, index, &sz);
n->v.string = stringindex(ctx, str, sz);
n->valuetype = VALUETYPE_STRING;
break;
}
case LUA_TBOOLEAN:
n->v.boolean = lua_toboolean(L, index);
n->valuetype = VALUETYPE_BOOLEAN;
break;
case LUA_TTABLE: {
struct table *tbl = ctx->tbl;
ctx->tbl = (struct table *)malloc(sizeof(struct table));
if (ctx->tbl == NULL) {
ctx->tbl = tbl;
luaL_error(L, "memory error");
// never get here
}
memset(ctx->tbl, 0, sizeof(struct table));
int absidx = lua_absindex(L, index);
lua_pushcfunction(L, convtable);
lua_pushvalue(L, absidx);
lua_pushlightuserdata(L, ctx);
lua_call(L, 2, 0);
n->v.tbl = ctx->tbl;
n->valuetype = VALUETYPE_TABLE;
ctx->tbl = tbl;
break;
}
default:
luaL_error(L, "Unsupport value type %s", lua_typename(L, vt));
break;
}
}
static void
setarray(struct context *ctx, lua_State *L, int index, int key) {
struct node n;
setvalue(ctx, L, index, &n);
struct table *tbl = ctx->tbl;
--key; // base 0
tbl->arraytype[key] = n.valuetype;
tbl->array[key] = n.v;
}
static int
ishashkey(struct context * ctx, lua_State *L, int index, int *key, uint32_t *keyhash, int *keytype) {
int sizearray = ctx->tbl->sizearray;
int kt = lua_type(L, index);
if (kt == LUA_TNUMBER) {
*key = lua_tointeger(L, index);
if (*key > 0 && *key <= sizearray) {
return 0;
}
*keyhash = (uint32_t)*key;
*keytype = KEYTYPE_INTEGER;
} else {
size_t sz = 0;
const char * s = lua_tolstring(L, index, &sz);
*keyhash = calchash(s, sz);
*key = stringindex(ctx, s, sz);
*keytype = KEYTYPE_STRING;
}
return 1;
}
static void
fillnocolliding(lua_State *L, struct context *ctx) {
struct table * tbl = ctx->tbl;
lua_pushnil(L);
while (lua_next(L, 1) != 0) {
int key;
int keytype;
uint32_t keyhash;
if (!ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) {
setarray(ctx, L, -1, key);
} else {
struct node * n = &tbl->hash[keyhash % tbl->sizehash];
if (n->valuetype == VALUETYPE_NIL) {
n->key = key;
n->keytype = keytype;
n->keyhash = keyhash;
n->next = -1;
n->nocolliding = 1;
setvalue(ctx, L, -1, n); // set n->v , n->valuetype
}
}
lua_pop(L,1);
}
}
static void
fillcolliding(lua_State *L, struct context *ctx) {
struct table * tbl = ctx->tbl;
int sizehash = tbl->sizehash;
int emptyslot = 0;
int i;
lua_pushnil(L);
while (lua_next(L, 1) != 0) {
int key;
int keytype;
uint32_t keyhash;
if (ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) {
struct node * mainpos = &tbl->hash[keyhash % tbl->sizehash];
if (!(mainpos->keytype == keytype && mainpos->key == key)) {
// the key has not insert
struct node * n = NULL;
for (i=emptyslot;i<sizehash;i++) {
if (tbl->hash[i].valuetype == VALUETYPE_NIL) {
n = &tbl->hash[i];
break;
}
}
assert(n);
n->next = mainpos->next;
mainpos->next = n - tbl->hash;
mainpos->nocolliding = 0;
n->key = key;
n->keytype = keytype;
n->keyhash = keyhash;
n->nocolliding = 0;
setvalue(ctx, L, -1, n); // set n->v , n->valuetype
}
}
lua_pop(L,1);
}
}
// table need convert
// struct context * ctx
static int
convtable(lua_State *L) {
int i;
struct context *ctx = lua_touserdata(L,2);
struct table *tbl = ctx->tbl;
tbl->L = ctx->L;
int sizearray = lua_rawlen(L, 1);
if (sizearray) {
tbl->arraytype = (uint8_t *)malloc(sizearray * sizeof(uint8_t));
if (tbl->arraytype == NULL) {
goto memerror;
}
for (i=0;i<sizearray;i++) {
tbl->arraytype[i] = VALUETYPE_NIL;
}
tbl->array = (union value *)malloc(sizearray * sizeof(union value));
if (tbl->array == NULL) {
goto memerror;
}
tbl->sizearray = sizearray;
}
int sizehash = countsize(L, sizearray);
if (sizehash) {
tbl->hash = (struct node *)malloc(sizehash * sizeof(struct node));
if (tbl->hash == NULL) {
goto memerror;
}
for (i=0;i<sizehash;i++) {
tbl->hash[i].valuetype = VALUETYPE_NIL;
tbl->hash[i].nocolliding = 0;
}
tbl->sizehash = sizehash;
fillnocolliding(L, ctx);
fillcolliding(L, ctx);
} else {
int i;
for (i=1;i<=sizearray;i++) {
lua_rawgeti(L, 1, i);
setarray(ctx, L, -1, i);
lua_pop(L,1);
}
}
return 0;
memerror:
return luaL_error(L, "memory error");
}
static void
delete_tbl(struct table *tbl) {
int i;
for (i=0;i<tbl->sizearray;i++) {
if (tbl->arraytype[i] == VALUETYPE_TABLE) {
delete_tbl(tbl->array[i].tbl);
}
}
for (i=0;i<tbl->sizehash;i++) {
if (tbl->hash[i].valuetype == VALUETYPE_TABLE) {
delete_tbl(tbl->hash[i].v.tbl);
}
}
free(tbl->arraytype);
free(tbl->array);
free(tbl->hash);
free(tbl);
}
static int
pconv(lua_State *L) {
struct context *ctx = lua_touserdata(L,1);
lua_State * pL = lua_touserdata(L, 2);
int ret;
lua_settop(L, 0);
// init L (may throw memory error)
// create a table for string map
lua_newtable(L);
lua_pushcfunction(pL, convtable);
lua_pushvalue(pL,1);
lua_pushlightuserdata(pL, ctx);
ret = lua_pcall(pL, 2, 0, 0);
if (ret != LUA_OK) {
size_t sz = 0;
const char * error = lua_tolstring(pL, -1, &sz);
lua_pushlstring(L, error, sz);
lua_error(L);
// never get here
}
luaL_checkstack(L, ctx->string_index + 3, NULL);
lua_settop(L,1);
return 1;
}
static void
convert_stringmap(struct context *ctx, struct table *tbl) {
lua_State *L = ctx->L;
lua_settop(L, ctx->string_index + 1);
lua_pushvalue(L, 1);
struct state * s = lua_newuserdata(L, sizeof(*s));
s->dirty = 0;
s->ref = 0;
s->root = tbl;
lua_replace(L, 1);
lua_replace(L, -2);
lua_pushnil(L);
// ... stringmap nil
while (lua_next(L, -2) != 0) {
int idx = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_pushvalue(L, -1);
lua_replace(L, idx);
}
lua_pop(L, 1);
lua_gc(L, LUA_GCCOLLECT, 0);
}
static int
lnewconf(lua_State *L) {
int ret;
struct context ctx;
struct table * tbl = NULL;
luaL_checktype(L,1,LUA_TTABLE);
ctx.L = luaL_newstate();
ctx.tbl = NULL;
ctx.string_index = 1; // 1 reserved for dirty flag
if (ctx.L == NULL) {
lua_pushliteral(L, "memory error");
goto error;
}
tbl = (struct table *)malloc(sizeof(struct table));
if (tbl == NULL) {
// lua_pushliteral may fail because of memory error, close first.
lua_close(ctx.L);
ctx.L = NULL;
lua_pushliteral(L, "memory error");
goto error;
}
memset(tbl, 0, sizeof(struct table));
ctx.tbl = tbl;
lua_pushcfunction(ctx.L, pconv);
lua_pushlightuserdata(ctx.L , &ctx);
lua_pushlightuserdata(ctx.L , L);
ret = lua_pcall(ctx.L, 2, 1, 0);
if (ret != LUA_OK) {
size_t sz = 0;
const char * error = lua_tolstring(ctx.L, -1, &sz);
lua_pushlstring(L, error, sz);
goto error;
}
convert_stringmap(&ctx, tbl);
lua_pushlightuserdata(L, tbl);
return 1;
error:
if (ctx.L) {
lua_close(ctx.L);
}
if (tbl) {
delete_tbl(tbl);
}
lua_error(L);
return -1;
}
static struct table *
get_table(lua_State *L, int index) {
struct table *tbl = lua_touserdata(L,index);
if (tbl == NULL) {
luaL_error(L, "Need a conf object");
}
return tbl;
}
static int
ldeleteconf(lua_State *L) {
struct table *tbl = get_table(L,1);
lua_close(tbl->L);
delete_tbl(tbl);
return 0;
}
static void
pushvalue(lua_State *L, lua_State *sL, uint8_t vt, union value *v) {
switch(vt) {
case VALUETYPE_NUMBER:
lua_pushnumber(L, v->n);
break;
case VALUETYPE_STRING: {
size_t sz = 0;
const char *str = lua_tolstring(sL, v->string, &sz);
lua_pushlstring(L, str, sz);
break;
}
case VALUETYPE_BOOLEAN:
lua_pushboolean(L, v->boolean);
break;
case VALUETYPE_TABLE:
lua_pushlightuserdata(L, v->tbl);
break;
default:
lua_pushnil(L);
break;
}
}
static struct node *
lookup_key(struct table *tbl, uint32_t keyhash, int key, int keytype, const char *str, size_t sz) {
if (tbl->sizehash == 0)
return NULL;
struct node *n = &tbl->hash[keyhash % tbl->sizehash];
if (keyhash != n->keyhash && n->nocolliding)
return NULL;
for (;;) {
if (keyhash == n->keyhash) {
if (n->keytype == KEYTYPE_INTEGER) {
if (keytype == KEYTYPE_INTEGER && n->key == key) {
return n;
}
} else {
// n->keytype == KEYTYPE_STRING
if (keytype == KEYTYPE_STRING) {
size_t sz2 = 0;
const char * str2 = lua_tolstring(tbl->L, n->key, &sz2);
if (sz == sz2 && memcmp(str,str2,sz) == 0) {
return n;
}
}
}
}
if (n->next < 0) {
return NULL;
}
n = &tbl->hash[n->next];
}
}
static int
lindexconf(lua_State *L) {
struct table *tbl = get_table(L,1);
int kt = lua_type(L,2);
uint32_t keyhash;
int key = 0;
int keytype;
size_t sz = 0;
const char * str = NULL;
if (kt == LUA_TNUMBER) {
lua_Number k = lua_tonumber(L, 2);
key = (int)k;
if ((lua_Number)key != k) {
return luaL_error(L, "Invalid key %f", k);
}
if (key > 0 && key <= tbl->sizearray) {
--key;
pushvalue(L, tbl->L, tbl->arraytype[key], &tbl->array[key]);
return 1;
}
keytype = KEYTYPE_INTEGER;
keyhash = (uint32_t)key;
} else {
str = luaL_checklstring(L, 2, &sz);
keyhash = calchash(str, sz);
keytype = KEYTYPE_STRING;
}
struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz);
if (n) {
pushvalue(L, tbl->L, n->valuetype, &n->v);
return 1;
} else {
return 0;
}
}
static void
pushkey(lua_State *L, lua_State *sL, struct node *n) {
if (n->keytype == KEYTYPE_INTEGER) {
lua_pushinteger(L, n->key);
} else {
size_t sz = 0;
const char * str = lua_tolstring(sL, n->key, &sz);
lua_pushlstring(L, str, sz);
}
}
static int
pushfirsthash(lua_State *L, struct table * tbl) {
if (tbl->sizehash) {
pushkey(L, tbl->L, &tbl->hash[0]);
return 1;
} else {
return 0;
}
}
static int
lnextkey(lua_State *L) {
struct table *tbl = get_table(L,1);
if (lua_isnoneornil(L,2)) {
if (tbl->sizearray > 0) {
int i;
for (i=0;i<tbl->sizearray;i++) {
if (tbl->arraytype[i] != VALUETYPE_NIL) {
lua_pushinteger(L, i+1);
return 1;
}
}
}
return pushfirsthash(L, tbl);
}
int kt = lua_type(L,2);
uint32_t keyhash;
int key = 0;
int keytype;
size_t sz=0;
const char *str = NULL;
int sizearray = tbl->sizearray;
if (kt == LUA_TNUMBER) {
lua_Number k = lua_tonumber(L,2);
key = (int)k;
if ((lua_Number)key != k) {
return 0;
}
if (key > 0 && key <= sizearray) {
int i;
for (i=key;i<sizearray;i++) {
if (tbl->arraytype[i] != VALUETYPE_NIL) {
lua_pushinteger(L, i+1);
return 1;
}
}
return pushfirsthash(L, tbl);
}
keyhash = (uint32_t)key;
keytype = KEYTYPE_INTEGER;
} else {
str = luaL_checklstring(L, 2, &sz);
keyhash = calchash(str, sz);
keytype = KEYTYPE_STRING;
}
struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz);
if (n) {
++n;
int index = n-tbl->hash;
if (index == tbl->sizehash) {
return 0;
}
pushkey(L, tbl->L, n);
return 1;
} else {
return 0;
}
}
static int
llen(lua_State *L) {
struct table *tbl = get_table(L,1);
lua_pushinteger(L, tbl->sizearray);
return 1;
}
static int
lhashlen(lua_State *L) {
struct table *tbl = get_table(L,1);
lua_pushinteger(L, tbl->sizehash);
return 1;
}
static int
releaseobj(lua_State *L) {
struct ctrl *c = lua_touserdata(L, 1);
struct table *tbl = c->root;
struct state *s = lua_touserdata(tbl->L, 1);
__sync_fetch_and_sub(&s->ref, 1);
c->root = NULL;
c->update = NULL;
return 0;
}
static int
lboxconf(lua_State *L) {
struct table * tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
__sync_fetch_and_add(&s->ref, 1);
struct ctrl * c = lua_newuserdata(L, sizeof(*c));
c->root = tbl;
c->update = NULL;
if (luaL_newmetatable(L, "confctrl")) {
lua_pushcfunction(L, releaseobj);
lua_setfield(L, -2, "__gc");
}
lua_setmetatable(L, -2);
return 1;
}
static int
lmarkdirty(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
s->dirty = 1;
return 0;
}
static int
lisdirty(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
int d = s->dirty;
lua_pushboolean(L, d);
return 1;
}
static int
lgetref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
lua_pushinteger(L , s->ref);
return 1;
}
static int
lincref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
int ref = __sync_add_and_fetch(&s->ref, 1);
lua_pushinteger(L , ref);
return 1;
}
static int
ldecref(lua_State *L) {
struct table *tbl = get_table(L,1);
struct state * s = lua_touserdata(tbl->L, 1);
int ref = __sync_sub_and_fetch(&s->ref, 1);
lua_pushinteger(L , ref);
return 1;
}
static int
lneedupdate(lua_State *L) {
struct ctrl * c = lua_touserdata(L, 1);
if (c->update) {
lua_pushlightuserdata(L, c->update);
lua_getuservalue(L, 1);
return 2;
}
return 0;
}
static int
lupdate(lua_State *L) {
luaL_checktype(L, 1, LUA_TUSERDATA);
luaL_checktype(L, 2, LUA_TLIGHTUSERDATA);
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");
}
lua_settop(L, 3);
lua_setuservalue(L, 1);
c->update = n;
return 0;
}
int
luaopen_sharedata_core(lua_State *L) {
luaL_Reg l[] = {
// used by host
{ "new", lnewconf },
{ "delete", ldeleteconf },
{ "markdirty", lmarkdirty },
{ "getref", lgetref },
{ "incref", lincref },
{ "decref", ldecref },
// used by client
{ "box", lboxconf },
{ "index", lindexconf },
{ "nextkey", lnextkey },
{ "len", llen },
{ "hashlen", lhashlen },
{ "isdirty", lisdirty },
{ "needupdate", lneedupdate },
{ "update", lupdate },
{ NULL, NULL },
};
luaL_checkversion(L);
luaL_newlib(L, l);
return 1;
}

View File

@@ -73,10 +73,17 @@ _cb(struct skynet_context * context, void * ud, int type, int session, uint32_t
return 0;
}
static int
forward_cb(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
_cb(context, ud, type, session, source, msg, sz);
// don't delete msg in forward mode.
return 1;
}
static int
_callback(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
int forward = lua_toboolean(L, 2);
luaL_checktype(L,1,LUA_TFUNCTION);
lua_settop(L,1);
lua_rawsetp(L, LUA_REGISTRYINDEX, _cb);
@@ -84,7 +91,11 @@ _callback(lua_State *L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
lua_State *gL = lua_tothread(L,-1);
skynet_callback(context, gL, _cb);
if (forward) {
skynet_callback(context, gL, forward_cb);
} else {
skynet_callback(context, gL, _cb);
}
return 0;
}
@@ -115,44 +126,13 @@ _genid(lua_State *L) {
return 1;
}
// copy from _send
static int
_sendname(lua_State *L, struct skynet_context * context, const char * dest) {
int type = luaL_checkinteger(L, 2);
int session = 0;
if (lua_isnil(L,3)) {
type |= PTYPE_TAG_ALLOCSESSION;
} else {
session = luaL_checkinteger(L,3);
static const char *
get_dest_string(lua_State *L, int index) {
const char * dest_string = lua_tostring(L, index);
if (dest_string == NULL) {
luaL_error(L, "dest address type (%s) must be a string or number.", lua_typename(L, lua_type(L,index)));
}
int mtype = lua_type(L,4);
switch (mtype) {
case LUA_TSTRING: {
size_t len = 0;
void * msg = (void *)lua_tolstring(L,4,&len);
session = skynet_sendname(context, dest, type, session , msg, len);
break;
}
case LUA_TNIL :
session = skynet_sendname(context, dest, type, session , NULL, 0);
break;
case LUA_TLIGHTUSERDATA: {
luaL_checktype(L, 4, LUA_TLIGHTUSERDATA);
void * msg = lua_touserdata(L,4);
int size = luaL_checkinteger(L,5);
session = skynet_sendname(context, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
break;
}
default:
luaL_error(L, "skynet.send invalid param %s", lua_typename(L,lua_type(L,4)));
}
if (session < 0) {
luaL_error(L, "skynet.send session (%d) < 0", session);
}
lua_pushinteger(L,session);
return 1;
return dest_string;
}
/*
@@ -167,28 +147,10 @@ _sendname(lua_State *L, struct skynet_context * context, const char * dest) {
static int
_send(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
int addr_type = lua_type(L,1);
uint32_t dest = 0;
switch(addr_type) {
case LUA_TNUMBER:
dest = lua_tounsigned(L,1);
break;
case LUA_TSTRING: {
const char * addrname = lua_tostring(L,1);
if (addrname[0] == '.' || addrname[0] == ':') {
dest = skynet_queryname(context, addrname);
if (dest == 0) {
luaL_error(L, "Invalid name %s", addrname);
}
} else if ('0' <= addrname[0] && addrname[0] <= '9') {
luaL_error(L, "Invalid name %s: must not start with a digit", addrname);
} else {
return _sendname(L, context, addrname);
}
break;
}
default:
return luaL_error(L, "address must be number or string, got %s",lua_typename(L,addr_type));
uint32_t dest = lua_tounsigned(L, 1);
const char * dest_string = NULL;
if (dest == 0) {
dest_string = get_dest_string(L, 1);
}
int type = luaL_checkinteger(L, 2);
@@ -207,13 +169,21 @@ _send(lua_State *L) {
if (len == 0) {
msg = NULL;
}
session = skynet_send(context, 0, dest, type, session , msg, len);
if (dest_string) {
session = skynet_sendname(context, 0, dest_string, type, session , msg, len);
} else {
session = skynet_send(context, 0, dest, type, session , msg, len);
}
break;
}
case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,4);
int size = luaL_checkinteger(L,5);
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
if (dest_string) {
session = skynet_sendname(context, 0, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
} else {
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
}
break;
}
default:
@@ -221,7 +191,7 @@ _send(lua_State *L) {
}
if (session < 0) {
// send to invalid address
// todo: maybe throw error is better
// todo: maybe throw error whould be better
return 0;
}
lua_pushinteger(L,session);
@@ -231,7 +201,11 @@ _send(lua_State *L) {
static int
_redirect(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
uint32_t dest = luaL_checkunsigned(L,1);
uint32_t dest = lua_tounsigned(L,1);
const char * dest_string = NULL;
if (dest == 0) {
dest_string = get_dest_string(L, 1);
}
uint32_t source = luaL_checkunsigned(L,2);
int type = luaL_checkinteger(L,3);
int session = luaL_checkinteger(L,4);
@@ -244,13 +218,21 @@ _redirect(lua_State *L) {
if (len == 0) {
msg = NULL;
}
session = skynet_send(context, source, dest, type, session , msg, len);
if (dest_string) {
session = skynet_sendname(context, source, dest_string, type, session , msg, len);
} else {
session = skynet_send(context, source, dest, type, session , msg, len);
}
break;
}
case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,5);
int size = luaL_checkinteger(L,6);
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
if (dest_string) {
session = skynet_sendname(context, source, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
} else {
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
}
break;
}
default:
@@ -289,8 +271,38 @@ _harbor(lua_State *L) {
return 2;
}
static int
lpackstring(lua_State *L) {
_luaseri_pack(L);
char * str = (char *)lua_touserdata(L, -2);
int sz = lua_tointeger(L, -1);
lua_pushlstring(L, str, sz);
skynet_free(str);
return 1;
}
static int
ltrash(lua_State *L) {
int t = lua_type(L,1);
switch (t) {
case LUA_TSTRING: {
break;
}
case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,1);
luaL_checkinteger(L,2);
skynet_free(msg);
break;
}
default:
luaL_error(L, "skynet.trash invalid param %s", lua_typename(L,t));
}
return 0;
}
int
luaopen_skynet_c(lua_State *L) {
luaopen_skynet_core(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
@@ -303,6 +315,8 @@ luaopen_skynet_c(lua_State *L) {
{ "harbor", _harbor },
{ "pack", _luaseri_pack },
{ "unpack", _luaseri_unpack },
{ "packstring", lpackstring },
{ "trash" , ltrash },
{ "callback", _callback },
{ NULL, NULL },
};

View File

@@ -14,6 +14,7 @@
#define BACKLOG 32
// 2 ** 12 == 4096
#define LARGE_PAGE_NODE 12
#define BUFFER_LIMIT (256 * 1024)
struct buffer_node {
char * msg;
@@ -245,6 +246,7 @@ lclearbuffer(lua_State *L) {
while(sb->head) {
return_free_node(L,2,sb);
}
sb->size = 0;
return 0;
}
@@ -263,6 +265,7 @@ lreadall(lua_State *L) {
return_free_node(L,2,sb);
}
luaL_pushresult(&b);
sb->size = 0;
return 1;
}
@@ -476,6 +479,14 @@ lstart(lua_State *L) {
return 0;
}
static int
lnodelay(lua_State *L) {
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
int id = luaL_checkinteger(L, 1);
skynet_socket_nodelay(ctx,id);
return 0;
}
int
luaopen_socketdriver(lua_State *L) {
luaL_checkversion(L);
@@ -502,6 +513,7 @@ luaopen_socketdriver(lua_State *L) {
{ "lsend", lsendlow },
{ "bind", lbind },
{ "start", lstart },
{ "nodelay", lnodelay },
{ NULL, NULL },
};
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");

244
lualib-src/lua-stm.c Normal file
View File

@@ -0,0 +1,244 @@
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include "rwlock.h"
#include "skynet_malloc.h"
struct stm_object {
struct rwlock lock;
int reference;
struct stm_copy * copy;
};
struct stm_copy {
int reference;
uint32_t sz;
void * msg;
};
// msg should alloc by skynet_malloc
static struct stm_copy *
stm_newcopy(void * msg, int32_t sz) {
struct stm_copy * copy = skynet_malloc(sizeof(*copy));
copy->reference = 1;
copy->sz = sz;
copy->msg = msg;
return copy;
}
static struct stm_object *
stm_new(void * msg, int32_t sz) {
struct stm_object * obj = skynet_malloc(sizeof(*obj));
rwlock_init(&obj->lock);
obj->reference = 1;
obj->copy = stm_newcopy(msg, sz);
return obj;
}
static void
stm_releasecopy(struct stm_copy *copy) {
if (copy == NULL)
return;
if (__sync_sub_and_fetch(&copy->reference, 1) == 0) {
skynet_free(copy->msg);
skynet_free(copy);
}
}
static void
stm_release(struct stm_object *obj) {
assert(obj->copy);
rwlock_wlock(&obj->lock);
// writer release the stm object, so release the last copy .
stm_releasecopy(obj->copy);
obj->copy = NULL;
if (--obj->reference > 0) {
// stm object grab by readers, reset the copy to NULL.
rwlock_wunlock(&obj->lock);
return;
}
// no one grab the stm object, no need to unlock wlock.
skynet_free(obj);
}
static void
stm_releasereader(struct stm_object *obj) {
rwlock_rlock(&obj->lock);
if (__sync_sub_and_fetch(&obj->reference,1) == 0) {
// last reader, no writer. so no need to unlock
assert(obj->copy == NULL);
skynet_free(obj);
return;
}
rwlock_runlock(&obj->lock);
}
static void
stm_grab(struct stm_object *obj) {
rwlock_rlock(&obj->lock);
int ref = __sync_fetch_and_add(&obj->reference,1);
rwlock_runlock(&obj->lock);
assert(ref > 0);
}
static struct stm_copy *
stm_copy(struct stm_object *obj) {
rwlock_rlock(&obj->lock);
struct stm_copy * ret = obj->copy;
if (ret) {
int ref = __sync_fetch_and_add(&ret->reference,1);
assert(ref > 0);
}
rwlock_runlock(&obj->lock);
return ret;
}
static void
stm_update(struct stm_object *obj, void *msg, int32_t sz) {
struct stm_copy *copy = stm_newcopy(msg, sz);
rwlock_wlock(&obj->lock);
struct stm_copy *oldcopy = obj->copy;
obj->copy = copy;
rwlock_wunlock(&obj->lock);
stm_releasecopy(oldcopy);
}
// lua binding
struct boxstm {
struct stm_object * obj;
};
static int
lcopy(lua_State *L) {
struct boxstm * box = lua_touserdata(L, 1);
stm_grab(box->obj);
lua_pushlightuserdata(L, box->obj);
return 1;
}
static int
lnewwriter(lua_State *L) {
void * msg = lua_touserdata(L, 1);
uint32_t sz = luaL_checkunsigned(L, 2);
struct boxstm * box = lua_newuserdata(L, sizeof(*box));
box->obj = stm_new(msg,sz);
lua_pushvalue(L, lua_upvalueindex(1));
lua_setmetatable(L, -2);
return 1;
}
static int
ldeletewriter(lua_State *L) {
struct boxstm * box = lua_touserdata(L, 1);
stm_release(box->obj);
box->obj = NULL;
return 0;
}
static int
lupdate(lua_State *L) {
struct boxstm * box = lua_touserdata(L, 1);
void * msg = lua_touserdata(L, 2);
uint32_t sz = luaL_checkunsigned(L, 3);
stm_update(box->obj, msg, sz);
return 0;
}
struct boxreader {
struct stm_object *obj;
struct stm_copy *lastcopy;
};
static int
lnewreader(lua_State *L) {
struct boxreader * box = lua_newuserdata(L, sizeof(*box));
box->obj = lua_touserdata(L, 1);
box->lastcopy = NULL;
lua_pushvalue(L, lua_upvalueindex(1));
lua_setmetatable(L, -2);
return 1;
}
static int
ldeletereader(lua_State *L) {
struct boxreader * box = lua_touserdata(L, 1);
stm_releasereader(box->obj);
box->obj = NULL;
stm_releasecopy(box->lastcopy);
box->lastcopy = NULL;
return 0;
}
static int
lread(lua_State *L) {
struct boxreader * box = lua_touserdata(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
struct stm_copy * copy = stm_copy(box->obj);
if (copy == box->lastcopy) {
// not update
stm_releasecopy(copy);
lua_pushboolean(L, 0);
return 1;
}
stm_releasecopy(box->lastcopy);
box->lastcopy = copy;
if (copy) {
lua_settop(L, 2);
lua_pushlightuserdata(L, copy->msg);
lua_pushunsigned(L, copy->sz);
lua_call(L, 2, LUA_MULTRET);
lua_pushboolean(L, 1);
lua_replace(L, 1);
return lua_gettop(L);
} else {
lua_pushboolean(L, 0);
return 1;
}
}
int
luaopen_stm(lua_State *L) {
luaL_checkversion(L);
lua_createtable(L, 0, 3);
lua_pushcfunction(L, lcopy);
lua_setfield(L, -2, "copy");
luaL_Reg writer[] = {
{ "new", lnewwriter },
{ NULL, NULL },
};
lua_createtable(L, 0, 2);
lua_pushcfunction(L, ldeletewriter),
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, lupdate),
lua_setfield(L, -2, "__call");
luaL_setfuncs(L, writer, 1);
luaL_Reg reader[] = {
{ "newcopy", lnewreader },
{ NULL, NULL },
};
lua_createtable(L, 0, 2);
lua_pushcfunction(L, ldeletereader),
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, lread),
lua_setfield(L, -2, "__call");
luaL_setfuncs(L, reader, 1);
return 1;
}

View File

@@ -4,12 +4,24 @@ local clusterd
local cluster = {}
function cluster.call(node, address, ...)
-- skynet.pack(...) will free by cluster.c.packrequest
-- skynet.pack(...) will free by cluster.core.packrequest
return skynet.call(clusterd, "lua", "req", node, address, skynet.pack(...))
end
function cluster.open(port)
skynet.call(clusterd, "lua", "listen", "0.0.0.0", port)
if type(port) == "string" then
skynet.call(clusterd, "lua", "listen", port)
else
skynet.call(clusterd, "lua", "listen", "0.0.0.0", port)
end
end
function cluster.reload()
skynet.call(clusterd, "lua", "reload")
end
function cluster.proxy(node, name)
return skynet.call(clusterd, "lua", "proxy", node, name)
end
skynet.init(function()

View File

@@ -10,5 +10,9 @@ function datacenter.set(...)
return skynet.call("DATACENTER", "lua", "UPDATE", ...)
end
function datacenter.wait(...)
return skynet.call("DATACENTER", "lua", "WAIT", ...)
end
return datacenter

114
lualib/http/httpc.lua Normal file
View File

@@ -0,0 +1,114 @@
local socket = require "http.sockethelper"
local url = require "http.url"
local internal = require "http.internal"
local string = string
local table = table
local httpc = {}
local function request(fd, method, host, url, recvheader, header, content)
local read = socket.readfunc(fd)
local write = socket.writefunc(fd)
local header_content = ""
if header then
for k,v in pairs(header) do
header_content = string.format("%s%s:%s\r\n", header_content, k, v)
end
end
if content then
local data = string.format("%s %s HTTP/1.1\r\nhost:%s\r\ncontent-length:%d\r\n%s\r\n%s", method, url, host, #content, header_content, content)
write(data)
else
local request_header = string.format("%s %s HTTP/1.1\r\nhost:%s\r\ncontent-length:0\r\n%s\r\n", method, url, host, header_content)
write(request_header)
end
local tmpline = {}
local body = internal.recvheader(read, tmpline, "")
if not body then
error(socket.socket_error)
end
local statusline = tmpline[1]
local code, info = statusline:match "HTTP/[%d%.]+%s+([%d]+)%s+(.*)$"
code = assert(tonumber(code))
local header = internal.parseheader(tmpline,2,recvheader or {})
if not header then
error("Invalid HTTP response header")
end
local length = header["content-length"]
if length then
length = tonumber(length)
end
local mode = header["transfer-encoding"]
if mode then
if mode ~= "identity" and mode ~= "chunked" then
error ("Unsupport transfer-encoding")
end
end
if mode == "chunked" then
body, header = internal.recvchunkedbody(read, nil, header, body)
if not body then
error("Invalid response body")
end
else
-- identity mode
if length then
if #body >= length then
body = body:sub(1,length)
else
local padding = read(length - #body)
body = body .. padding
end
else
body = nil
end
end
return code, body
end
function httpc.request(method, host, url, recvheader, header, content)
local hostname, port = host:match"([^:]+):?(%d*)$"
if port == "" then
port = 80
else
port = tonumber(port)
end
local fd = socket.connect(hostname, port)
local ok , statuscode, body = pcall(request, fd,method, host, url, recvheader, header, content)
if ok then
return statuscode, body
else
socket.close(fd)
error(statuscode)
end
end
function httpc.get(...)
return httpc.request("GET", ...)
end
local function escape(s)
return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
return string.format("%%%02X", string.byte(c))
end))
end
function httpc.post(host, url, form, recvheader)
local header = {
["content-type"] = "application/x-www-form-urlencoded"
}
local body = {}
for k,v in pairs(form) do
table.insert(body, string.format("%s=%s",escape(k),escape(v)))
end
return httpc.request("POST", host, url, recvheader, header, table.concat(body , "&"))
end
return httpc

145
lualib/http/httpd.lua Normal file
View File

@@ -0,0 +1,145 @@
local internal = require "http.internal"
local table = table
local httpd = {}
local http_status_msg = {
[100] = "Continue",
[101] = "Switching Protocols",
[200] = "OK",
[201] = "Created",
[202] = "Accepted",
[203] = "Non-Authoritative Information",
[204] = "No Content",
[205] = "Reset Content",
[206] = "Partial Content",
[300] = "Multiple Choices",
[301] = "Moved Permanently",
[302] = "Found",
[303] = "See Other",
[304] = "Not Modified",
[305] = "Use Proxy",
[307] = "Temporary Redirect",
[400] = "Bad Request",
[401] = "Unauthorized",
[402] = "Payment Required",
[403] = "Forbidden",
[404] = "Not Found",
[405] = "Method Not Allowed",
[406] = "Not Acceptable",
[407] = "Proxy Authentication Required",
[408] = "Request Time-out",
[409] = "Conflict",
[410] = "Gone",
[411] = "Length Required",
[412] = "Precondition Failed",
[413] = "Request Entity Too Large",
[414] = "Request-URI Too Large",
[415] = "Unsupported Media Type",
[416] = "Requested range not satisfiable",
[417] = "Expectation Failed",
[500] = "Internal Server Error",
[501] = "Not Implemented",
[502] = "Bad Gateway",
[503] = "Service Unavailable",
[504] = "Gateway Time-out",
[505] = "HTTP Version not supported",
}
local function readall(readbytes, bodylimit)
local tmpline = {}
local body = internal.recvheader(readbytes, tmpline, "")
if not body then
return 413 -- Request Entity Too Large
end
local request = assert(tmpline[1])
local method, url, httpver = request:match "^(%a+)%s+(.-)%s+HTTP/([%d%.]+)$"
assert(method and url and httpver)
httpver = assert(tonumber(httpver))
if httpver < 1.0 or httpver > 1.1 then
return 505 -- HTTP Version not supported
end
local header = internal.parseheader(tmpline,2,{})
if not header then
return 400 -- Bad request
end
local length = header["content-length"]
if length then
length = tonumber(length)
end
local mode = header["transfer-encoding"]
if mode then
if mode ~= "identity" and mode ~= "chunked" then
return 501 -- Not Implemented
end
end
if mode == "chunked" then
body, header = internal.recvchunkedbody(readbytes, bodylimit, header, body)
if not body then
return 413
end
else
-- identity mode
if length then
if length > bodylimit then
return 413
end
if #body >= length then
body = body:sub(1,length)
else
local padding = readbytes(length - #body)
body = body .. padding
end
end
end
return 200, url, method, header, body
end
function httpd.read_request(...)
local ok, code, url, method, header, body = pcall(readall, ...)
if ok then
return code, url, method, header, body
else
return nil, code
end
end
local function writeall(writefunc, statuscode, bodyfunc, header)
local statusline = string.format("HTTP/1.1 %03d %s\r\n", statuscode, http_status_msg[statuscode] or "")
writefunc(statusline)
if header then
for k,v in pairs(header) do
writefunc(string.format("%s: %s\r\n", k,v))
end
end
local t = type(bodyfunc)
if t == "string" then
writefunc(string.format("content-length: %d\r\n\r\n", #bodyfunc))
writefunc(bodyfunc)
elseif t == "function" then
writefunc("transfer-encoding: chunked\r\n")
while true do
local s = bodyfunc()
if s then
if s ~= "" then
writefunc(string.format("\r\n%x\r\n", #s))
writefunc(s)
end
else
writefunc("\r\n0\r\n\r\n")
end
end
else
assert(t == "nil")
writefunc("\r\n")
end
end
function httpd.write_response(...)
return pcall(writeall, ...)
end
return httpd

135
lualib/http/internal.lua Normal file
View File

@@ -0,0 +1,135 @@
local M = {}
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
end
end
local function readcrln(readbytes, body)
if #body >= 2 then
if body:sub(1,2) ~= "\r\n" then
return
end
return body:sub(3)
else
body = body .. readbytes(2-#body)
if body ~= "\r\n" then
return
end
return ""
end
end
function M.recvheader(readbytes, lines, header)
if #header >= 2 then
if header:find "^\r\n" then
return header:sub(3)
end
end
local result
local e = header:find("\r\n\r\n", 1, true)
if e then
result = header:sub(e+4)
else
while true do
local bytes = readbytes()
header = header .. bytes
if #header > LIMIT then
return
end
e = header:find("\r\n\r\n", -#bytes-3, true)
if e then
result = header:sub(e+4)
break
end
if header:find "^\r\n" then
return header:sub(3)
end
end
end
for v in header:gmatch("(.-)\r\n") do
if v == "" then
break
end
table.insert(lines, v)
end
return result
end
function M.parseheader(lines, from, header)
local name, value
for i=from,#lines do
local line = lines[i]
if line:byte(1) == 9 then -- tab, append last line
if name == nil then
return
end
header[name] = header[name] .. line:sub(2)
else
name, value = line:match "^(.-):%s*(.*)"
if name == nil or value == nil then
return
end
name = name:lower()
if header[name] then
header[name] = header[name] .. ", " .. value
else
header[name] = value
end
end
end
return header
end
function M.recvchunkedbody(readbytes, bodylimit, header, body)
local result = ""
local size = 0
while true do
local sz
sz , body = chunksize(readbytes, body)
if not sz then
return
end
if sz == 0 then
break
end
size = size + sz
if bodylimit and size > bodylimit then
return
end
if #body >= sz then
result = result .. body:sub(1,sz)
body = body:sub(sz+1)
else
result = result .. body .. readbytes(sz - #body)
body = ""
end
body = readcrln(readbytes, body)
if not body then
return
end
end
local tmpline = {}
body = M.recvheader(readbytes, tmpline, body)
if not body then
return
end
header = M.parseheader(tmpline,1,header)
return result, header
end
return M

View File

@@ -0,0 +1,43 @@
local socket = require "socket"
local readbytes = socket.read
local writebytes = socket.write
local sockethelper = {}
local socket_error = setmetatable({} , { __tostring = function() return "[Socket Error]" end })
sockethelper.socket_error = socket_error
function sockethelper.readfunc(fd)
return function (sz)
local ret = readbytes(fd, sz)
if ret then
return ret
else
error(socket_error)
end
end
end
function sockethelper.writefunc(fd)
return function(content)
local ok = writebytes(fd, content)
if not ok then
error(socket_error)
end
end
end
function sockethelper.connect(host, port)
local fd = socket.open(host, port)
if fd then
return fd
end
error(socket_error)
end
function sockethelper.close(fd)
socket.close(fd)
end
return sockethelper

28
lualib/http/url.lua Normal file
View File

@@ -0,0 +1,28 @@
local url = {}
local function decode_func(c)
return string.char(tonumber(c, 16))
end
local function decode(str)
local str = str:gsub('+', ' ')
return str:gsub("%%(..)", decode_func)
end
function url.parse(u)
local path,query = u:match "([^?]*)%??(.*)"
if path then
path = decode(path)
end
return path, query
end
function url.parse_query(q)
local r = {}
for k,v in q:gmatch "(.-)=([^&]*)&?" do
r[decode(k)] = decode(v)
end
return r
end
return url

View File

@@ -3,11 +3,13 @@ for word in string.gmatch(..., "%S+") do
table.insert(args, word)
end
SERVICE_NAME = args[1]
local main, pattern
local err = {}
for pat in string.gmatch(LUA_SERVICE, "([^;]+);*") do
local filename = string.gsub(pat, "?", args[1])
local filename = string.gsub(pat, "?", SERVICE_NAME)
local f, msg = loadfile(filename)
if not f then
table.insert(err, msg)

View File

@@ -1,96 +1,125 @@
local bson = require "bson"
local socket = require "socket"
local socketchannel = require "socketchannel"
local socketchannel = require "socketchannel"
local skynet = require "skynet"
local driver = require "mongo.driver"
local md5 = require "md5"
local md5 = require "md5"
local rawget = rawget
local assert = assert
local bson_encode = bson.encode
local bson_encode_order = bson.encode_order
local bson_decode = bson.decode
local bson_encode = bson.encode
local bson_encode_order = bson.encode_order
local bson_decode = bson.decode
local empty_bson = bson_encode {}
local mongo = {}
local mongo = {}
mongo.null = assert(bson.null)
mongo.maxkey = assert(bson.maxkey)
mongo.minkey = assert(bson.minkey)
mongo.type = assert(bson.type)
local mongo_cursor = {}
local cursor_meta = {
__index = mongo_cursor,
local cursor_meta = {
__index = mongo_cursor,
}
local mongo_client = {}
local client_meta = {
__index = function(self, key)
return rawget(mongo_client, key) or self:getDB(key)
local client_meta = {
__index = function(self, key)
return rawget(mongo_client, key) or self:getDB(key)
end,
__tostring = function (self)
local port_string
if self.port then
port_string = ":" .. tostring(self.port)
port_string = ":" .. tostring(self.port)
else
port_string = ""
port_string = ""
end
return "[mongo client : " .. self.host .. port_string .."]"
return "[mongo client : " .. self.host .. port_string .."]"
end,
-- DO NOT need disconnect, because channel will shutdown during gc
-- DO NOT need disconnect, because channel will shutdown during gc
}
local mongo_db = {}
local db_meta = {
__index = function (self, key)
return rawget(mongo_db, key) or self:getCollection(key)
local db_meta = {
__index = function (self, key)
return rawget(mongo_db, key) or self:getCollection(key)
end,
__tostring = function (self)
return "[mongo db : " .. self.name .. "]"
return "[mongo db : " .. self.name .. "]"
end
}
local mongo_collection = {}
local collection_meta = {
__index = function(self, key)
return rawget(mongo_collection, key) or self:getCollection(key)
end ,
local collection_meta = {
__index = function(self, key)
return rawget(mongo_collection, key) or self:getCollection(key)
end ,
__tostring = function (self)
return "[mongo collection : " .. self.full_name .. "]"
return "[mongo collection : " .. self.full_name .. "]"
end
}
local function dispatch_reply(so)
local len_reply = so:read(4)
local reply = so:read(driver.length(len_reply))
local result = { result = {} }
local succ, reply_id, document, cursor_id, startfrom = driver.reply(reply, result.result)
result.document = document
local len_reply = so:read(4)
local reply = so:read(driver.length(len_reply))
local result = { result = {} }
local succ, reply_id, document, cursor_id, startfrom = driver.reply(reply, result.result)
result.document = document
result.cursor_id = cursor_id
result.startfrom = startfrom
result.data = reply
result.data = reply
return reply_id, succ, result
end
local function mongo_auth(mongoc)
local user = rawget(mongoc, "username")
local pass = rawget(mongoc, "password")
local function __parse_addr(addr)
local host, port = string.match(addr, "([^:]+):(.+)")
return host, tonumber(port)
end
local function mongo_auth(mongoc)
local user = rawget(mongoc, "username")
local pass = rawget(mongoc, "password")
if user == nil or pass == nil then
return
end
return function()
assert(mongoc:auth(user, pass))
if user ~= nil and pass ~= nil then
assert(mongoc:auth(user, pass))
end
local rs_data = mongoc:runCommand("ismaster")
if rs_data.ok == 1 then
if rs_data.hosts then
local backup = {}
for _, v in ipairs(rs_data.hosts) do
local host, port = __parse_addr(v)
table.insert(backup, {host = host, port = port})
end
mongoc.__sock:changebackup(backup)
end
if rs_data.ismaster then
return
else
local host, port = __parse_addr(rs_data.primary)
mongoc.host = host
mongoc.port = port
mongoc.__sock:changehost(host, port)
end
end
end
end
function mongo.client( conf )
local obj = {
host = conf.host,
port = conf.port or 27017,
function mongo.client( conf )
local first = conf
local backup = nil
if conf.rs then
first = conf.rs[1]
backup = conf.rs
end
local obj = {
host = first.host,
port = first.port or 27017,
}
obj.__id = 0
@@ -99,9 +128,10 @@ function mongo.client( conf )
port = obj.port,
response = dispatch_reply,
auth = mongo_auth(obj),
backup = backup,
}
setmetatable(obj, client_meta)
obj.__sock:connect(true) -- try connect only once
obj.__sock:connect(true) -- try connect only once
return obj
end
@@ -109,32 +139,32 @@ function mongo_client:getDB(dbname)
local db = {
connection = self,
name = dbname,
full_name = dbname,
full_name = dbname,
database = false,
__cmd = dbname .. "." .. "$cmd",
__cmd = dbname .. "." .. "$cmd",
}
db.database = db
db.database = db
return setmetatable(db, db_meta)
return setmetatable(db, db_meta)
end
function mongo_client:disconnect()
if self.__sock then
local so = self.__sock
self.__sock = false
self.__sock = false
so:close()
end
end
function mongo_client:genId()
local id = self.__id + 1
self.__id = id
self.__id = id
return id
end
function mongo_client:runCommand(...)
if not self.admin then
self.admin = self:getDB "admin"
self.admin = self:getDB "admin"
end
return self.admin:runCommand(...)
end
@@ -146,14 +176,14 @@ function mongo_client:auth(user,password)
return false
end
local key = md5.sumhexa(string.format("%s%s%s",result.nonce,user,password))
local key = md5.sumhexa(string.format("%s%s%s",result.nonce,user,password))
local result= self:runCommand ("authenticate",1,"user",user,"nonce",result.nonce,"key",key)
return result.ok == 1
return result.ok == 1
end
function mongo_client:logout()
local result = self:runCommand "logout"
return result.ok == 1
return result.ok == 1
end
function mongo_db:runCommand(cmd,cmd_v,...)
@@ -166,18 +196,18 @@ function mongo_db:runCommand(cmd,cmd_v,...)
else
bson_cmd = bson_encode_order(cmd,cmd_v,...)
end
local pack = driver.query(request_id, 0, self.__cmd, 0, 1, bson_cmd)
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
local pack = driver.query(request_id, 0, self.__cmd, 0, 1, bson_cmd)
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
return bson_decode(doc)
end
function mongo_db:getCollection(collection)
local col = {
local col = {
connection = self.connection,
name = collection,
full_name = self.full_name .. "." .. collection,
full_name = self.full_name .. "." .. collection,
database = self.database,
}
self[collection] = setmetatable(col, collection_meta)
@@ -188,20 +218,20 @@ mongo_collection.getCollection = mongo_db.getCollection
function mongo_collection:insert(doc)
if doc._id == nil then
doc._id = bson.objectid()
doc._id = bson.objectid()
end
local sock = self.connection.__sock
local pack = driver.insert(0, self.full_name, bson_encode(doc))
-- flags support 1: ContinueOnError
-- flags support 1: ContinueOnError
sock:request(pack)
end
function mongo_collection:batch_insert(docs)
for i=1,#docs do
for i=1,#docs do
if docs[i]._id == nil then
docs[i]._id = bson.objectid()
docs[i]._id = bson.objectid()
end
docs[i] = bson_encode(docs[i])
docs[i] = bson_encode(docs[i])
end
local sock = self.connection.__sock
local pack = driver.insert(0, self.full_name, docs)
@@ -209,7 +239,7 @@ function mongo_collection:batch_insert(docs)
end
function mongo_collection:update(selector,update,upsert,multi)
local flags = (upsert and 1 or 0) + (multi and 2 or 0)
local flags = (upsert and 1 or 0) + (multi and 2 or 0)
local sock = self.connection.__sock
local pack = driver.update(self.full_name, flags, bson_encode(selector), bson_encode(update))
sock:request(pack)
@@ -225,24 +255,24 @@ function mongo_collection:findOne(query, selector)
local conn = self.connection
local request_id = conn:genId()
local sock = conn.__sock
local pack = driver.query(request_id, 0, self.full_name, 0, 1, query and bson_encode(query) or empty_bson, selector and bson_encode(selector))
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
local pack = driver.query(request_id, 0, self.full_name, 0, 1, query and bson_encode(query) or empty_bson, selector and bson_encode(selector))
-- we must hold req (req.data), because req.document is a lightuserdata, it's a pointer to the string (req.data)
local req = sock:request(pack, request_id)
local doc = req.document
return bson_decode(doc)
end
function mongo_collection:find(query, selector)
return setmetatable( {
__collection = self,
__query = query and bson_encode(query) or empty_bson,
__query = query and bson_encode(query) or empty_bson,
__selector = selector and bson_encode(selector),
__ptr = nil,
__ptr = nil,
__data = nil,
__cursor = nil,
__document = {},
__flags = 0,
} , cursor_meta)
__flags = 0,
} , cursor_meta)
end
function mongo_cursor:hasNext()
@@ -255,42 +285,42 @@ function mongo_cursor:hasNext()
local sock = conn.__sock
local pack
if self.__data == nil then
pack = driver.query(request_id, self.__flags, self.__collection.full_name,0,0,self.__query,self.__selector)
pack = driver.query(request_id, self.__flags, self.__collection.full_name,0,0,self.__query,self.__selector)
else
if self.__cursor then
pack = driver.more(request_id, self.__collection.full_name,0,self.__cursor)
else
-- no more
self.__document = nil
self.__data = nil
self.__document = nil
self.__data = nil
return false
end
end
local ok, result = pcall(sock.request,sock,pack, request_id)
local doc = result.document
local doc = result.document
local cursor = result.cursor_id
if ok then
if doc then
self.__document = result.result
self.__data = result.data
self.__document = result.result
self.__data = result.data
self.__ptr = 1
self.__cursor = cursor
self.__cursor = cursor
return true
else
self.__document = nil
self.__data = nil
self.__cursor = nil
self.__document = nil
self.__data = nil
self.__cursor = nil
return false
end
else
self.__document = nil
self.__data = nil
self.__cursor = nil
self.__document = nil
self.__data = nil
self.__cursor = nil
if doc then
local err = bson_decode(doc)
local err = bson_decode(doc)
error(err["$err"])
else
error("Reply from mongod error")
@@ -303,11 +333,11 @@ end
function mongo_cursor:next()
if self.__ptr == nil then
error "Call hasNext first"
error "Call hasNext first"
end
local r = bson_decode(self.__document[self.__ptr])
self.__ptr = self.__ptr + 1
if self.__ptr > #self.__document then
local r = bson_decode(self.__document[self.__ptr])
self.__ptr = self.__ptr + 1
if self.__ptr > #self.__document then
self.__ptr = nil
end

View File

@@ -1,5 +1,7 @@
-- This is a deprecated module, use skynet.queue instead.
local skynet = require "skynet"
local c = require "skynet.c"
local c = require "skynet.core"
local mqueue = {}
local init_once

View File

@@ -1,5 +1,5 @@
local skynet = require "skynet"
local mc = require "multicast.c"
local mc = require "multicast.core"
local multicastd
local multicast = {}

View File

@@ -1,6 +1,7 @@
local skynet = require "skynet"
local socket = require "socket"
local socketchannel = require "socketchannel"
local int64 = require "int64"
local table = table
local string = string
@@ -94,31 +95,56 @@ function command:disconnect()
setmetatable(self, nil)
end
local function compose_message(msg)
if #msg == 1 then
return msg[1] .. "\r\n"
-- msg could be any type of value
local function pack_value(lines, v)
if v == nil then
return
end
local lines = { "*" .. #msg }
for _,v in ipairs(msg) do
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
table.insert(lines,"")
local cmd = table.concat(lines,"\r\n")
return cmd
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
local function compose_message(cmd, msg)
local len = 1
local t = type(msg)
if t == "table" then
len = len + #msg
elseif t ~= nil then
len = len + 1
end
local lines = {"*" .. len}
pack_value(lines, cmd)
if t == "table" then
for _,v in ipairs(msg) do
pack_value(lines, v)
end
else
pack_value(lines, msg)
end
table.insert(lines, "")
local chunk = table.concat(lines,"\r\n")
return chunk
end
setmetatable(command, { __index = function(t,k)
local cmd = string.upper(k)
local f = function (self, ...)
return self[1]:request(compose_message { cmd, ... }, read_response)
local f = function (self, v, ...)
if type(v) == "table" then
return self[1]:request(compose_message(cmd, v), read_response)
else
return self[1]:request(compose_message(cmd, {v, ...}), read_response)
end
end
t[k] = f
return f
@@ -131,12 +157,12 @@ end
function command:exists(key)
local fd = self[1]
return fd:request(compose_message { "EXISTS", key }, read_boolean)
return fd:request(compose_message ("EXISTS", key), read_boolean)
end
function command:sismember(key, value)
local fd = self[1]
return fd:request(compose_message { "SISMEMBER", key, value }, read_boolean)
return fd:request(compose_message ("SISMEMBER", {key, value}), read_boolean)
end
--- watch mode
@@ -156,10 +182,10 @@ local function watch_login(obj, auth)
so:request("AUTH "..auth.."\r\n", read_response)
end
for k in pairs(obj.__psubscribe) do
so:request(compose_message { "PSUBSCRIBE", k })
so:request(compose_message ("PSUBSCRIBE", k))
end
for k in pairs(obj.__subscribe) do
so:request(compose_message { "SUBSCRIBE", k })
so:request(compose_message("SUBSCRIBE", k))
end
end
end
@@ -192,7 +218,7 @@ local function watch_func( name )
local so = self.__sock
for i = 1, select("#", ...) do
local v = select(i, ...)
so:request(compose_message { NAME, v })
so:request(compose_message(NAME, v))
end
end
end

43
lualib/sharedata.lua Normal file
View File

@@ -0,0 +1,43 @@
local skynet = require "skynet"
local sd = require "sharedata.corelib"
local service
skynet.init(function()
service = skynet.uniqueservice "sharedatad"
end)
local sharedata = {}
local function monitor(name, obj, cobj)
local newobj = cobj
while true do
newobj = skynet.call(service, "lua", "monitor", name, newobj)
if newobj == nil then
break
end
sd.update(obj, newobj)
end
end
function sharedata.query(name)
local obj = skynet.call(service, "lua", "query", name)
local r = sd.box(obj)
skynet.send(service, "lua", "confirm" , obj)
skynet.fork(monitor,name, r, obj)
return r
end
function sharedata.new(name, v)
skynet.call(service, "lua", "new", name, v)
end
function sharedata.update(name, v)
skynet.call(service, "lua", "update", name, v)
end
function sharedata.delete(name)
skynet.call(service, "lua", "delete", name)
end
return sharedata

View File

@@ -0,0 +1,134 @@
local core = require "sharedata.core"
local type = type
local next = next
local rawget = rawget
local conf = {}
conf.host = {
new = core.new,
delete = core.delete,
getref = core.getref,
markdirty = core.markdirty,
incref = core.incref,
decref = core.decref,
}
local meta = {}
local isdirty = core.isdirty
local index = core.index
local needupdate = core.needupdate
local len = core.len
local function findroot(self)
while self.__parent do
self = self.__parent
end
return self
end
local function update(root, cobj, gcobj)
root.__obj = cobj
root.__gcobj = gcobj
-- don't use pairs
for k,v in next, root do
if type(v)=="table" and k~="__parent" then
local pointer = index(cobj, k)
if type(pointer) == "userdata" then
update(v, pointer, gcobj)
else
root[k] = nil
end
end
end
end
local function genkey(self)
local key = tostring(self.__key)
while self.__parent do
self = self.__parent
key = self.__key .. "." .. key
end
return key
end
function meta:__index(key)
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
end
end
local v = index(self.__obj, key)
if type(v) == "userdata" then
local r = setmetatable({
__obj = v,
__gcobj = self.__gcobj,
__parent = self,
__key = key,
}, meta)
self[key] = r
return r
else
return v
end
end
function meta:__len()
return len(self.__obj)
end
local function conf_ipairs(self, index)
local obj = self.__obj
index = index + 1
local value = rawget(self, index)
if value then
return index, value
end
local sz = len(obj)
if sz < index then
return
end
return index, self[index]
end
function meta:__ipairs()
return conf_ipairs, self, 0
end
function meta:__pairs()
return conf.next, self, nil
end
function conf.next(obj, key)
local nextkey = core.nextkey(obj.__obj, key)
if nextkey then
return nextkey, obj[nextkey]
end
end
function conf.box(obj)
local gcobj = core.box(obj)
return setmetatable({
__parent = false,
__obj = obj,
__gcobj = gcobj,
__key = "",
} , meta)
end
function conf.update(self, pointer)
local cobj = self.__obj
assert(isdirty(cobj), "Obly dirty object can be update")
core.update(self.__gcobj, pointer, { __gcobj = core.box(pointer) })
end
return conf

View File

@@ -1,4 +1,4 @@
local c = require "skynet.c"
local c = require "skynet.core"
local tostring = tostring
local tonumber = tonumber
local coroutine = coroutine
@@ -22,7 +22,7 @@ local skynet = {
PTYPE_HARBOR = 5,
PTYPE_SOCKET = 6,
PTYPE_ERROR = 7,
PTYPE_QUEUE = 8,
PTYPE_QUEUE = 8, -- use in deprecated mqueue, use skynet.queue instead
PTYPE_DEBUG = 9,
PTYPE_LUA = 10,
PTYPE_SNAX = 11,
@@ -43,12 +43,14 @@ end
local session_id_coroutine = {}
local session_coroutine_id = {}
local session_coroutine_address = {}
local session_response = {}
local wakeup_session = {}
local sleep_session = {}
local watching_service = {}
local watching_session = {}
local dead_service = {}
local error_queue = {}
-- suspend is function
@@ -73,7 +75,9 @@ local function _error_dispatch(error_session, error_source)
if error_session == 0 then
-- service is down
-- Don't remove from watching_service , because user may call dead service
watching_service[error_source] = false
if watching_service[error_source] then
dead_service[error_source] = true
end
for session, srv in pairs(watching_session) do
if srv == error_source then
table.insert(error_queue, session)
@@ -95,6 +99,7 @@ local coroutine_yield = coroutine.yield
local function co_create(f)
local co = table.remove(coroutine_pool)
if co == nil then
local print = print
co = coroutine.create(function(...)
f(...)
while true do
@@ -122,12 +127,24 @@ local function dispatch_wakeup()
end
end
local function release_watching(address)
local ref = watching_service[address]
if ref then
ref = ref - 1
if ref > 0 then
watching_service[address] = ref
else
watching_service[address] = nil
end
end
end
-- suspend is local function
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 and session ~= 0 then
if session then
c.send(addr, skynet.PTYPE_ERROR, session, "")
end
session_coroutine_id[co] = nil
@@ -142,15 +159,70 @@ function suspend(co, result, command, param, size)
elseif command == "RETURN" then
local co_session = session_coroutine_id[co]
local co_address = session_coroutine_address[co]
if param == nil then
if param == nil or session_response[co] then
error(debug.traceback(co))
end
c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size)
return suspend(co, coroutine.resume(co))
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
elseif size == nil then
c.trash(param, size)
ret = false
end
return suspend(co, coroutine.resume(co, ret))
elseif command == "RESPONSE" then
local co_session = session_coroutine_id[co]
local co_address = session_coroutine_address[co]
if session_response[co] then
error(debug.traceback(co))
end
local f = param
local function response(ok, ...)
if ok == "TEST" then
if dead_service[co_address] then
release_watching(co_address)
f = false
return false
else
return true
end
end
if not f then
if f == false then
f = nil
return false
end
error "Can't response more than once"
end
local ret
if not dead_service[co_address] then
if ok then
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, f(...)) >=0
else
ret = c.send(co_address, skynet.PTYPE_ERROR, co_session, "") >=0
end
else
ret = false
end
release_watching(co_address)
f = nil
return ret
end
watching_service[co_address] = watching_service[co_address] + 1
session_response[co] = response
return suspend(co, coroutine.resume(co, response))
elseif command == "EXIT" then
-- coroutine exit
local address = session_coroutine_address[co]
release_watching(address)
session_coroutine_id[co] = nil
session_coroutine_address[co] = nil
session_response[co] = nil
elseif command == "QUIT" then
-- service exit
return
else
error("Unknown command : " .. command .. "\n" .. debug.traceback(co))
end
@@ -190,12 +262,33 @@ function skynet.wait()
session_id_coroutine[session] = nil
end
local function globalname(name, handle)
local c = string.sub(name,1,1)
assert(c ~= ':')
if c == '.' then
return false
end
assert(#name <= 16) -- GLOBALNAME_LENGTH is 16, defined in skynet_harbor.h
assert(tonumber(name) == nil) -- global name can't be number
local harbor = require "skynet.harbor"
harbor.globalname(name, handle)
return true
end
function skynet.register(name)
c.command("REG", name)
if not globalname(name) then
c.command("REG", name)
end
end
function skynet.name(name, handle)
c.command("NAME", name .. " " .. skynet.address(handle))
if not globalname(name, handle) then
c.command("NAME", name .. " " .. skynet.address(handle))
end
end
local self_handle
@@ -208,7 +301,10 @@ function skynet.self()
end
function skynet.localname(name)
return string_to_handle(c.command("QUERY", name))
local addr = c.command("QUERY", name)
if addr then
return string_to_handle(addr)
end
end
function skynet.launch(...)
@@ -226,9 +322,30 @@ function skynet.starttime()
return tonumber(c.command("STARTTIME"))
end
function skynet.time()
return skynet.now()/100 + skynet.starttime() -- get now first would be better
end
function skynet.exit()
skynet.send(".launcher","lua","REMOVE",skynet.self())
-- report the sources that call me
for co, session in pairs(session_coroutine_id) do
local address = session_coroutine_address[co]
if session~=0 and address then
c.redirect(address, 0, skynet.PTYPE_ERROR, session, "")
end
end
-- report the sources I call but haven't return
local tmp = {}
for session, address in pairs(watching_session) do
tmp[address] = true
end
for address in pairs(tmp) do
c.redirect(address, 0, skynet.PTYPE_ERROR, 0, "")
end
c.command("EXIT")
-- quit service
coroutine_yield "QUIT"
end
function skynet.kill(name)
@@ -254,9 +371,6 @@ end
function skynet.send(addr, typename, ...)
local p = proto[typename]
if watching_service[addr] == false then
error("Service is dead")
end
return c.send(addr, p.id, 0 , p.pack(...))
end
@@ -267,6 +381,7 @@ skynet.redirect = function(dest,source,typename,...)
end
skynet.pack = assert(c.pack)
skynet.packstring = assert(c.packstring)
skynet.unpack = assert(c.unpack)
skynet.tostring = assert(c.tostring)
@@ -280,9 +395,6 @@ end
function skynet.call(addr, typename, ...)
local p = proto[typename]
if watching_service[addr] == false then
error("Service is dead")
end
local session = c.send(addr, p.id , nil , p.pack(...))
if session == nil then
error("call to invalid address " .. skynet.address(addr))
@@ -298,7 +410,12 @@ end
function skynet.ret(msg, sz)
msg = msg or ""
coroutine_yield("RETURN", msg, sz)
return coroutine_yield("RETURN", msg, sz)
end
function skynet.response(pack)
pack = pack or skynet.pack
return coroutine_yield("RESPONSE", pack)
end
function skynet.retpack(...)
@@ -318,8 +435,8 @@ function skynet.dispatch(typename, func)
p.dispatch = func
end
local function unknown_request(session, address, msg, sz)
print("Unknown request :" , c.tostring(msg,sz))
local function unknown_request(session, address, msg, sz, prototype)
skynet.error(string.format("Unknown request (%s): %s", prototype, c.tostring(msg,sz)))
error(string.format("Unknown session : %d from %x", session, address))
end
@@ -368,12 +485,18 @@ local function raw_dispatch_message(prototype, msg, sz, session, source, ...)
local p = assert(proto[prototype], prototype)
local f = p.dispatch
if f then
local ref = watching_service[source]
if ref then
watching_service[source] = ref + 1
else
watching_service[source] = 1
end
local co = co_create(f)
session_coroutine_id[co] = session
session_coroutine_address[co] = source
suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...)))
else
unknown_request(session, source, msg, sz)
unknown_request(session, source, msg, sz, proto[prototype])
end
end
end
@@ -426,7 +549,7 @@ end
function skynet.address(addr)
if type(addr) == "number" then
return string.format(":%x",addr)
return string.format(":%08x",addr)
else
return tostring(addr)
end
@@ -502,7 +625,7 @@ end
local function init_service(start)
local ok, err = xpcall(init_template, debug.traceback, start)
if not ok then
print("init service failed:", err)
skynet.error("init service failed: " .. tostring(err))
skynet.send(".launcher","lua", "ERROR")
skynet.exit()
else
@@ -526,6 +649,21 @@ function skynet.filter(f ,start_func)
end)
end
function skynet.forward_type(map, start_func)
c.callback(function(ptype, msg, sz, ...)
local prototype = map[ptype]
if prototype then
dispatch_message(prototype, msg, sz, ...)
else
dispatch_message(ptype, msg, sz, ...)
c.trash(msg, sz)
end
end, true)
skynet.timeout(0, function()
init_service(start_func)
end)
end
function skynet.endless()
return c.command("ENDLESS")~=nil
end
@@ -543,14 +681,26 @@ function skynet.monitor(service, query)
end
assert(monitor, "Monitor launch failed")
c.command("MONITOR", string.format(":%08x", monitor))
return monitor
end
function skynet.mqlen()
return tonumber(c.command "MQLEN")
end
function skynet.task(ret)
local t = 0
for session,co in pairs(session_id_coroutine) do
if ret then
ret[session] = debug.traceback(co)
end
t = t + 1
end
return t
end
-- Inject internal debug framework
local debug = require "skynet.debug"
debug(skynet)
debug(skynet, dispatch_message)
return skynet

View File

@@ -1,4 +1,8 @@
return function (skynet)
local io = io
local table = table
local debug = debug
return function (skynet, dispatch_func)
local internal_info_func
@@ -21,9 +25,16 @@ end
function dbgcmd.STAT()
local stat = {}
stat.mqlen = skynet.mqlen()
stat.task = skynet.task()
skynet.ret(skynet.pack(stat))
end
function dbgcmd.TASK()
local task = {}
skynet.task(task)
skynet.ret(skynet.pack(task))
end
function dbgcmd.INFO()
if internal_info_func then
skynet.ret(skynet.pack(internal_info_func()))
@@ -32,6 +43,17 @@ function dbgcmd.INFO()
end
end
function dbgcmd.EXIT()
skynet.exit()
end
function dbgcmd.RUN(source, filename)
local inject = require "skynet.inject"
local output = inject(source, filename , dispatch_func, skynet.register_protocol)
collectgarbage "collect"
skynet.ret(skynet.pack(table.concat(output, "\n")))
end
local function _debug_dispatch(session, address, cmd, ...)
local f = dbgcmd[cmd]
assert(f, cmd)
@@ -46,4 +68,4 @@ skynet.register_protocol {
dispatch = _debug_dispatch,
}
end
end

22
lualib/skynet/harbor.lua Normal file
View File

@@ -0,0 +1,22 @@
local skynet = require "skynet"
local harbor = {}
function harbor.globalname(name, handle)
handle = handle or skynet.self()
skynet.send(".cslave", "lua", "REGISTER", name, handle)
end
function harbor.queryname(name)
return skynet.call(".cslave", "lua", "QUERYNAME", name)
end
function harbor.link(id)
skynet.call(".cslave", "lua", "LINK", id)
end
function harbor.connect(id)
skynet.call(".cslave", "lua", "CONNECT", id)
end
return harbor

65
lualib/skynet/inject.lua Normal file
View File

@@ -0,0 +1,65 @@
local function getupvaluetable(u, func, unique)
i = 1
while true do
local name, value = debug.getupvalue(func, i)
if name == nil then
return
end
local t = type(value)
if t == "table" then
u[name] = value
elseif t == "function" then
if not unique[value] then
unique[value] = true
getupvaluetable(u, value, unique)
end
end
i=i+1
end
end
return function(source, filename , ...)
if filename then
filename = "@" .. filename
else
filename = "=(load)"
end
local output = {}
local function print(...)
local value = { ... }
for k,v in ipairs(value) do
value[k] = tostring(v)
end
table.insert(output, table.concat(value, "\t"))
end
local u = {}
local unique = {}
local funcs = { ... }
for k, func in ipairs(funcs) do
getupvaluetable(u, func, unique)
end
local p = {}
local proto = u.proto
if proto then
for k,v in pairs(proto) do
local name, dispatch = v.name, v.dispatch
if name and dispatch then
local pp = {}
p[name] = pp
getupvaluetable(pp, dispatch, unique)
end
end
end
local env = setmetatable( { print = print , _U = u, _P = p}, { __index = _ENV })
local func, err = load(source, filename, "bt", env)
if not func then
return { err }
end
local ok, err = xpcall(func, debug.traceback)
if not ok then
table.insert(output, err)
end
return output
end

33
lualib/skynet/queue.lua Normal file
View File

@@ -0,0 +1,33 @@
local skynet = require "skynet"
local coroutine = coroutine
local pcall = pcall
local table = table
function skynet.queue()
local current_thread
local ref = 0
local thread_queue = {}
return function(f, ...)
local thread = coroutine.running()
if ref == 0 then
current_thread = thread
elseif current_thread ~= thread then
table.insert(thread_queue, thread)
skynet.wait()
assert(ref == 0)
end
ref = ref + 1
local ok, err = pcall(f, ...)
ref = ref - 1
if ref == 0 then
current_thread = nil
local co = table.remove(thread_queue,1)
if co then
skynet.wakeup(co)
end
end
assert(ok,err)
end
end
return skynet.queue

130
lualib/snax/gateserver.lua Normal file
View File

@@ -0,0 +1,130 @@
local skynet = require "skynet"
local netpack = require "netpack"
local socketdriver = require "socketdriver"
local gateserver = {}
local socket -- listen socket
local queue -- message queue
local maxclient -- max client
local client_number = 0
local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local nodelay = false
local connection = {}
function gateserver.openclient(fd)
if connection[fd] then
socketdriver.start(fd)
end
end
function gateserver.closeclient(fd)
local c = connection[fd]
if c then
connection[fd] = false
socketdriver.close(fd)
end
end
function gateserver.start(handler)
assert(handler.message)
assert(handler.connect)
function CMD.open( source, conf )
assert(not socket)
local address = conf.address or "0.0.0.0"
local port = assert(conf.port)
maxclient = conf.maxclient or 1024
nodelay = conf.nodelay
socket = socketdriver.listen(address, port)
socketdriver.start(socket)
if handler.open then
return handler.open(source, conf)
end
end
function CMD.close()
assert(socket)
socketdriver.close(socket)
socket = nil
end
local MSG = {}
function MSG.data(fd, msg, sz)
if connection[fd] then
handler.message(fd, 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)
end
end
end
function MSG.open(fd, msg)
if client_number >= maxclient then
socketdriver.close(fd)
return
end
if nodelay then
socketdriver.nodelay(fd)
end
connection[fd] = true
client_number = client_number + 1
handler.connect(fd, msg)
end
local function close_fd(fd)
local c = connection[fd]
if c ~= nil then
connection[fd] = nil
client_number = client_number - 1
end
end
function MSG.close(fd)
if handler.disconnect then
handler.disconnect(fd)
end
close_fd(fd)
end
function MSG.error(fd, msg)
if handler.error then
handler.error(fd)
end
close_fd(fd)
end
skynet.register_protocol {
name = "socket",
id = skynet.PTYPE_SOCKET, -- PTYPE_SOCKET = 6
unpack = function ( msg, sz )
return netpack.filter( queue, msg, sz)
end,
dispatch = function (_, _, q, type, ...)
queue = q
if type then
MSG[type](...)
end
end
}
skynet.start(function()
skynet.dispatch("lua", function (_, address, cmd, ...)
local f = CMD[cmd]
if f then
skynet.ret(skynet.pack(f(address, ...)))
else
skynet.ret(skynet.pack(handler.command(cmd, address, ...)))
end
end)
end)
end
return gateserver

View File

@@ -3,7 +3,21 @@ local io = io
local hotfix = {}
local function collect_uv(f , uv)
local function envid(f)
local i = 1
while true do
local name, value = debug.getupvalue(f, i)
if name == nil then
return
end
if name == "_ENV" then
return debug.upvalueid(f, i)
end
i = i + 1
end
end
local function collect_uv(f , uv, env)
local i = 1
while true do
local name, value = debug.getupvalue(f, i)
@@ -18,7 +32,9 @@ local function collect_uv(f , uv)
uv[name] = { func = f, index = i, id = id }
if type(value) == "function" then
collect_uv(value, uv)
if envid(value) == env then
collect_uv(value, uv, env)
end
end
end
@@ -30,7 +46,7 @@ local function collect_all_uv(funcs)
local global = {}
for _, v in pairs(funcs) do
if v[4] then
collect_uv(v[4], global)
collect_uv(v[4], global, envid(v[4]))
end
end

View File

@@ -61,6 +61,8 @@ return function (name , G, loader)
setmetatable(G, { __index = env , __newindex = init_system })
local pattern
do
local path = skynet.getenv "snax"
@@ -70,6 +72,7 @@ return function (name , G, loader)
local filename = string.gsub(pat, "?", name)
local f , err = loader(filename, "bt", G)
if f then
pattern = pat
mainfunc = f
break
else
@@ -90,5 +93,5 @@ return function (name , G, loader)
G[k] = v
end
return func
return func, pattern
end

191
lualib/snax/loginserver.lua Normal file
View File

@@ -0,0 +1,191 @@
local skynet = require "skynet"
local socket = require "socket"
local crypt = require "crypt"
local table = table
local string = string
local assert = assert
--[[
Protocol:
line (\n) based text protocol
1. Server->Client : base64(8bytes random challenge)
2. Client->Server : base64(8bytes handshake client key)
3. Server: Gen a 8bytes handshake server key
4. Server->Client : base64(DH-Exchange(server key))
5. Server/Client secret := DH-Secret(client key/server key)
6. Client->Server : base64(HMAC(challenge, secret))
7. Client->Server : DES(secret, base64(token))
8. Server : call auth_handler(token) -> server, uid (A user defined method)
9. Server : call login_handler(server, uid, secret) ->subid (A user defined method)
10. Server->Client : 200 base64(subid)
Error Code:
400 Bad Request . challenge failed
401 Unauthorized . unauthorized by auth_handler
403 Forbidden . login_handler failed
406 Not Acceptable . already in login (disallow multi login)
Success:
200 base64(subid)
]]
local socket_error = {}
local function assert_socket(v, fd)
if v then
return v
else
skynet.error(string.format("auth failed: socket (fd = %d) closed", fd))
error(socket_error)
end
end
local function write(fd, text)
assert_socket(socket.write(fd, text), fd)
end
local function launch_slave(auth_handler)
local function auth(fd, addr)
fd = assert(tonumber(fd))
skynet.error(string.format("connect from %s (fd = %d)", addr, fd))
socket.start(fd)
-- set socket buffer limit (8K)
-- If the attacker send large package, close the socket
socket.limit(fd, 8192)
local challenge = crypt.randomkey()
write(fd, crypt.base64encode(challenge).."\n")
local handshake = assert_socket(socket.readline(fd), fd)
local clientkey = crypt.base64decode(handshake)
if #clientkey ~= 8 then
error "Invalid client key"
end
local serverkey = crypt.randomkey()
write(fd, crypt.base64encode(crypt.dhexchange(serverkey)).."\n")
local secret = crypt.dhsecret(clientkey, serverkey)
local response = assert_socket(socket.readline(fd), fd)
local hmac = crypt.hmac64(challenge, secret)
if hmac ~= crypt.base64decode(response) then
write(fd, "400 Bad Request\n")
error "challenge failed"
end
local etoken = assert_socket(socket.readline(fd),fd)
local token = crypt.desdecode(secret, crypt.base64decode(etoken))
local ok, server, uid = pcall(auth_handler,token)
socket.abandon(fd)
return ok, server, uid, secret
end
local function ret_pack(ok, err, ...)
if ok then
skynet.ret(skynet.pack(err, ...))
elseif err ~= socket_error then
error(err)
end
end
skynet.dispatch("lua", function(_,_,...)
ret_pack(pcall(auth, ...))
end)
end
local user_login = {}
local function accept(conf, s, fd, addr)
-- call slave auth
local ok, server, uid, secret = skynet.call(s, "lua", fd, addr)
socket.start(fd)
if not ok then
write(fd, "401 Unauthorized\n")
error(server)
end
if not conf.multilogin then
if user_login[uid] then
write(fd, "406 Not Acceptable\n")
error(string.format("User %s is already login", uid))
end
user_login[uid] = true
end
local ok, err = pcall(conf.login_handler, server, uid, secret)
-- unlock login
user_login[uid] = nil
if ok then
err = err or ""
write(fd, "200 "..crypt.base64encode(err).."\n")
else
write(fd, "403 Forbidden\n")
error(err)
end
end
local function launch_master(conf)
local instance = conf.instance or 8
assert(instance > 0)
local host = conf.host or "0.0.0.0"
local port = assert(tonumber(conf.port))
local slave = {}
local balance = 1
skynet.dispatch("lua", function(_,source,command, ...)
skynet.ret(skynet.pack(conf.command_handler(command, ...)))
end)
for i=1,instance do
table.insert(slave, skynet.newservice(SERVICE_NAME))
end
skynet.error(string.format("login server listen at : %s %d", host, port))
local id = socket.listen(host, port)
socket.start(id , function(fd, addr)
local s = slave[balance]
balance = balance + 1
if balance > #slave then
balance = 1
end
local ok, err = pcall(accept, conf, s, fd, addr)
if not ok then
if err ~= socket_error then
skynet.error(string.format("invalid client (fd = %d) error = %s", fd, err))
end
end
socket.close(fd)
end)
end
local function login(conf)
local name = "." .. (conf.name or "login")
skynet.start(function()
local loginmaster = skynet.localname(name)
if loginmaster then
local auth_handler = assert(conf.auth_handler)
launch_master = nil
conf = nil
launch_slave(auth_handler)
else
launch_slave = nil
conf.auth_handler = nil
assert(conf.login_handler)
assert(conf.command_handler)
skynet.register(name)
launch_master(conf)
end
end)
end
return login

318
lualib/snax/msgserver.lua Normal file
View File

@@ -0,0 +1,318 @@
local skynet = require "skynet"
local gateserver = require "snax.gateserver"
local netpack = require "netpack"
local crypt = require "crypt"
local socketdriver = require "socketdriver"
local assert = assert
local b64encode = crypt.base64encode
local b64decode = crypt.base64decode
--[[
Protocol:
All the number type is big-endian
Shakehands (The first package)
Client -> Server :
base64(uid)@base64(server)#base64(subid):index:base64(hmac)
Server -> Client
XXX ErrorCode
404 User Not Found
403 Index Expired
401 Unauthorized
400 Bad Request
200 OK
Req-Resp
Client -> Server : Request
word size (Not include self)
string content (size-4)
dword session
Server -> Client : Response
word size (Not include self)
string content (size-5)
byte ok (1 is ok, 0 is error)
dword session
API:
server.userid(username)
return uid, subid, server
server.username(uid, subid, server)
return username
server.login(username, secret)
update user secret
server.logout(username)
user logout
server.ip(username)
return ip when connection establish, or nil
server.start(conf)
start server
Supported skynet command:
kick username (may used by loginserver)
login username secret (used by loginserver)
logout username (used by agent)
Config for server.start:
conf.expired_number : the number of the response message cached after sending out (default is 128)
conf.login_handler(uid, secret) -> subid : the function when a new user login, alloc a subid for it. (may call by login server)
conf.logout_handler(uid, subid) : the functon when a user logout. (may call by agent)
conf.kick_handler(uid, subid) : the functon when a user logout. (may call by login server)
conf.request_handler(username, session, msg, sz) : the function when recv a new request.
conf.register_handler(servername) : call when gate open
conf.disconnect_handler(username) : call when a connection disconnect (afk)
]]
local server = {}
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
}
local user_online = {}
local handshake = {}
local connection = {}
function server.userid(username)
-- base64(uid)@base64(server)#base64(subid)
local uid, servername, subid = username:match "([^@]*)@([^#]*)#(.*)"
return b64decode(uid), b64decode(subid), b64decode(servername)
end
function server.username(uid, subid, servername)
return string.format("%s@%s#%s", b64encode(uid), b64encode(servername), b64encode(tostring(subid)))
end
function server.logout(username)
local u = user_online[username]
user_online[username] = nil
if u.fd then
gateserver.closeclient(u.fd)
connection[u.fd] = nil
end
end
function server.login(username, secret)
assert(user_online[username] == nil)
user_online[username] = {
secret = secret,
version = 0,
index = 0,
username = username,
response = {}, -- response cache
}
end
function server.ip(username)
local u = user_online[username]
if u and u.fd then
return u.ip
end
end
function server.start(conf)
local expired_number = conf.expired_number or 128
local handler = {}
local CMD = {
login = assert(conf.login_handler),
logout = assert(conf.logout_handler),
kick = assert(conf.kick_handler),
}
function handler.command(cmd, source, ...)
local f = assert(CMD[cmd])
return f(...)
end
function handler.open(source, gateconf)
local servername = assert(gateconf.servername)
return conf.register_handler(servername)
end
function handler.connect(fd, addr)
handshake[fd] = addr
gateserver.openclient(fd)
end
function handler.disconnect(fd)
handshake[fd] = nil
local c = connection[fd]
if c then
c.fd = nil
connection[fd] = nil
if conf.disconnect_handler then
conf.disconnect_handler(c.username)
end
end
end
handler.error = handler.disconnect
-- atomic , no yield
local function do_auth(fd, message, addr)
local username, index, hmac = string.match(message, "([^:]*):([^:]*):([^:]*)")
local u = user_online[username]
if u == nil then
return "404 User Not Found"
end
local idx = assert(tonumber(index))
hmac = b64decode(hmac)
if idx <= u.version then
return "403 Index Expired"
end
local text = string.format("%s:%s", username, index)
local v = crypt.hmac64(crypt.hashkey(text), u.secret)
if v ~= hmac then
return "401 Unauthorized"
end
u.version = idx
u.fd = fd
u.ip = addr
connection[fd] = u
end
local function auth(fd, addr, msg, sz)
local message = netpack.tostring(msg, sz)
local ok, result = pcall(do_auth, fd, message, addr)
if not ok then
skynet.error(result)
result = "400 Bad Request"
end
local close = result ~= nil
if result == nil then
result = "200 OK"
end
socketdriver.send(fd, netpack.pack(result))
if close then
gateserver.closeclient(fd)
end
end
local request_handler = assert(conf.request_handler)
-- u.response is a struct { return_fd , response, version, index }
local function retire_response(u)
if u.index >= expired_number * 2 then
local max = 0
local response = u.response
for k,p in pairs(response) do
if p[1] == nil then
-- request complete, check expired
if p[4] < expired_number then
response[k] = nil
else
p[4] = p[4] - expired_number
if p[4] > max then
max = p[4]
end
end
end
end
u.index = max + 1
end
end
local function do_request(fd, msg, sz)
local u = assert(connection[fd], "invalid fd")
local msg_sz = sz - 4
local session = netpack.tostring(msg, sz, msg_sz)
local p = u.response[session]
if p then
-- session can be reuse in the same connection
if p[3] == u.version then
local last = u.response[session]
u.response[session] = nil
p = nil
if last[2] == nil then
local error_msg = string.format("Conflict session %s", crypt.hexencode(session))
skynet.error(error_msg)
error(error_msg)
end
end
end
if p == nil then
p = { fd }
u.response[session] = p
local ok, result = pcall(conf.request_handler, u.username, msg, msg_sz)
result = result or ""
-- NOTICE: YIELD here, socket may close.
if not ok then
skynet.error(result)
result = "\0" .. session
else
result = result .. '\1' .. session
end
p[2] = netpack.pack_string(result)
p[3] = u.version
p[4] = u.index
else
netpack.tostring(msg, sz) -- request before, so free msg
-- update version/index, change return fd.
-- resend response.
p[1] = fd
p[3] = u.version
p[4] = u.index
if p[2] == nil then
-- already request, but response is not ready
return
end
end
u.index = u.index + 1
-- the return fd is p[1] (fd may change by multi request) check connect
fd = p[1]
if connection[fd] then
socketdriver.send(fd, p[2])
end
p[1] = nil
retire_response(u)
end
local function request(fd, msg, sz)
local ok, err = pcall(do_request, fd, msg, sz)
-- not atomic, may yield
if not ok then
skynet.error(string.format("Invalid package %s : %s", err, netpack.tostring(msg, sz)))
if connection[fd] then
gateserver.closeclient(fd)
end
end
end
function handler.message(fd, msg, sz)
local addr = handshake[fd]
if addr then
auth(fd,addr,msg,sz)
handshake[fd] = nil
else
request(fd, msg, sz)
end
end
return gateserver.start(handler)
end
return server

View File

@@ -56,11 +56,19 @@ socket_message[1] = function(id, size, data)
s.read_required = nil
wakeup(s)
end
elseif rrt == "string" then
-- read line
if driver.readline(s.buffer,nil,rr) then
s.read_required = nil
wakeup(s)
else
if s.buffer_limit and sz > s.buffer_limit then
skynet.error(string.format("socket buffer overlow: fd=%d size=%d", id , sz))
driver.clear(s.buffer,buffer_pool)
driver.close(id)
return
end
if rrt == "string" then
-- read line
if driver.readline(s.buffer,nil,rr) then
s.read_required = nil
wakeup(s)
end
end
end
end
@@ -137,6 +145,8 @@ local function connect(id, func)
suspend(s)
if s.connected then
return id
else
socket_pool[id] = nil
end
end
@@ -199,6 +209,27 @@ end
function socket.read(id, sz)
local s = socket_pool[id]
assert(s)
if sz == nil then
-- read some bytes
local ret = driver.readall(s.buffer, buffer_pool)
if ret ~= "" then
return ret
end
if not s.connected then
return false, ret
end
assert(not s.read_required)
s.read_required = 0
suspend(s)
ret = driver.readall(s.buffer, buffer_pool)
if ret ~= "" then
return ret
else
return false, ret
end
end
local ret = driver.pop(s.buffer, buffer_pool, sz)
if ret then
return ret
@@ -272,7 +303,13 @@ function socket.invalid(id)
return socket_pool[id] == nil
end
socket.listen = assert(driver.listen)
function socket.listen(host, port, backlog)
if port == nil then
host, port = string.match(host, "([^:]+):(.+)$")
port = tonumber(port)
end
return driver.listen(host, port, backlog)
end
function socket.lock(id)
local s = socket_pool[id]
@@ -312,4 +349,9 @@ function socket.abandon(id)
socket_pool[id] = nil
end
function socket.limit(id, limit)
local s = assert(socket_pool[id])
s.buffer_limit = limit
end
return socket

View File

@@ -26,6 +26,7 @@ function socket_channel.channel(desc)
local c = {
__host = assert(desc.host),
__port = assert(desc.port),
__backup = desc.backup,
__auth = desc.auth,
__response = desc.response, -- It's for session mode
__request = {}, -- request seq { response func or session } -- It's for order mode
@@ -135,6 +136,9 @@ local function dispatch_by_order(self)
if result ~= socket_error then
errmsg = result_ok
end
self.__result[co] = socket_error
self.__result_data[co] = errmsg
skynet.wakeup(co)
wakeup_all(self, errmsg)
end
end
@@ -149,42 +153,74 @@ local function dispatch_function(self)
end
end
local function connect_backup(self)
if self.__backup then
for _, addr in ipairs(self.__backup) do
local host, port
if type(addr) == "table" then
host, port = addr.host, addr.port
else
host = addr
port = self.__port
end
skynet.error("socket: connect to backup host", host, port)
local fd = socket.open(host, port)
if fd then
self.__host = host
self.__port = port
return fd
end
end
end
end
local function connect_once(self)
if self.__closed then
return false
end
assert(not self.__sock and not self.__authcoroutine)
local fd = socket.open(self.__host, self.__port)
if not fd then
return false
fd = connect_backup(self)
if not fd then
return false
end
end
self.__authcoroutine = coroutine.running()
self.__sock = setmetatable( {fd} , channel_socket_meta )
skynet.fork(dispatch_function(self), self)
if self.__auth then
self.__authcoroutine = coroutine.running()
local ok , message = pcall(self.__auth, self)
if not ok then
close_channel_socket(self)
if message ~= socket_error then
self.__authcoroutine = false
skynet.error("socket: auth failed", message)
end
end
self.__authcoroutine = false
if ok and not self.__sock then
-- auth may change host, so connect again
return connect_once(self)
end
return ok
end
self.__authcoroutine = false
return true
end
local function try_connect(self , once)
local t = 100
local t = 0
while not self.__closed do
if connect_once(self) then
if not once then
skynet.error("socket: connect to", self.__host, self.__port)
end
return
return true
elseif once then
error(string.format("Connect to %s:%d failed", self.__host, self.__port))
return false
end
if t > 1000 then
skynet.error("socket: try to reconnect", self.__host, self.__port)
@@ -197,7 +233,7 @@ local function try_connect(self , once)
end
end
local function block_connect(self, once)
local function check_connection(self)
if self.__sock then
local authco = self.__authcoroutine
if not authco then
@@ -211,26 +247,36 @@ local function block_connect(self, once)
if self.__closed then
return false
end
end
local function block_connect(self, once)
local r = check_connection(self)
if r ~= nil then
return r
end
if #self.__connecting > 0 then
-- connecting in other coroutine
local co = coroutine.running()
table.insert(self.__connecting, co)
skynet.wait()
-- check connection again
return block_connect(self, once)
end
self.__connecting[1] = true
try_connect(self, once)
self.__connecting[1] = nil
for i=2, #self.__connecting do
local co = self.__connecting[i]
self.__connecting[i] = nil
skynet.wakeup(co)
else
self.__connecting[1] = true
try_connect(self, once)
self.__connecting[1] = nil
for i=2, #self.__connecting do
local co = self.__connecting[i]
self.__connecting[i] = nil
skynet.wakeup(co)
end
end
-- check again
return block_connect(self, once)
r = check_connection(self)
if r == nil then
error(string.format("Connect to %s:%d failed", self.__host, self.__port))
else
return r
end
end
function channel:connect(once)
@@ -260,7 +306,7 @@ local function wait_for_response(self, response)
end
function channel:request(request, response)
assert(block_connect(self))
assert(block_connect(self, true)) -- connect once
if not socket.write(self.__sock[1], request) then
close_channel_socket(self)
@@ -289,6 +335,20 @@ function channel:close()
end
end
function channel:changehost(host, port)
self.__host = host
if port then
self.__port = port
end
if not self.__closed then
close_channel_socket(self)
end
end
function channel:changebackup(backup)
self.__backup = backup
end
channel_meta.__gc = channel.close
local function wrapper_socket_function(f)

View File

@@ -31,7 +31,7 @@ macosx : EXPORT :=
macosx linux : SKYNET_LIBS += -ldl
linux freebsd : SKYNET_LIBS += -lrt
# Turn off jemalloc and malloc hook on macosx and freebsd
# Turn off jemalloc and malloc hook on macosx
macosx : MALLOC_STATICLIB :=
macosx : SKYNET_DEFINES :=-DNOUSE_JEMALLOC

View File

@@ -1,292 +0,0 @@
#include "skynet.h"
#include "skynet_harbor.h"
#include <string.h>
#include <assert.h>
#define HASH_SIZE 4096
#define DEFAULT_QUEUE_SIZE 1024
struct msg {
uint8_t * buffer;
size_t size;
};
struct msg_queue {
int size;
int head;
int tail;
struct msg * data;
};
struct keyvalue {
struct keyvalue * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
struct msg_queue * queue;
};
struct hashmap {
struct keyvalue *node[HASH_SIZE];
};
/*
message type (8bits) is in destination high 8bits
harbor id (8bits) is also in that place , but remote message doesn't need harbor id.
*/
struct remote_message_header {
uint32_t source;
uint32_t destination;
uint32_t session;
};
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
struct dummy {
struct skynet_context *ctx;
struct hashmap * map;
};
// hash table
static void
_push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct remote_message_header * header) {
// If there is only 1 free slot which is reserved to distinguish full/empty
// of circular buffer, expand it.
if (((queue->tail + 1) % queue->size) == queue->head) {
struct msg * new_buffer = skynet_malloc(queue->size * 2 * sizeof(struct msg));
int i;
for (i=0;i<queue->size-1;i++) {
new_buffer[i] = queue->data[(i+queue->head) % queue->size];
}
skynet_free(queue->data);
queue->data = new_buffer;
queue->head = 0;
queue->tail = queue->size - 1;
queue->size *= 2;
}
struct msg * slot = &queue->data[queue->tail];
queue->tail = (queue->tail + 1) % queue->size;
slot->buffer = skynet_malloc(sz + sizeof(*header));
memcpy(slot->buffer, buffer, sz);
memcpy(slot->buffer + sz, header, sizeof(*header));
slot->size = sz + sizeof(*header);
}
static struct msg *
_pop_queue(struct msg_queue * queue) {
if (queue->head == queue->tail) {
return NULL;
}
struct msg * slot = &queue->data[queue->head];
queue->head = (queue->head + 1) % queue->size;
return slot;
}
static struct msg_queue *
_new_queue() {
struct msg_queue * queue = skynet_malloc(sizeof(*queue));
queue->size = DEFAULT_QUEUE_SIZE;
queue->head = 0;
queue->tail = 0;
queue->data = skynet_malloc(DEFAULT_QUEUE_SIZE * sizeof(struct msg));
return queue;
}
static void
_release_queue(struct msg_queue *queue) {
if (queue == NULL)
return;
struct msg * m = _pop_queue(queue);
while (m) {
skynet_free(m->buffer);
m = _pop_queue(queue);
}
skynet_free(queue->data);
skynet_free(queue);
}
static struct keyvalue *
_hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t*) name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue * node = hash->node[h % HASH_SIZE];
while (node) {
if (node->hash == h && strncmp(node->key, name, GLOBALNAME_LENGTH) == 0) {
return node;
}
node = node->next;
}
return NULL;
}
static struct keyvalue *
_hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *)name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue ** pkv = &hash->node[h % HASH_SIZE];
struct keyvalue * node = skynet_malloc(sizeof(*node));
memcpy(node->key, name, GLOBALNAME_LENGTH);
node->next = *pkv;
node->queue = NULL;
node->hash = h;
node->value = 0;
*pkv = node;
return node;
}
static struct hashmap *
_hash_new() {
struct hashmap * h = skynet_malloc(sizeof(struct hashmap));
memset(h,0,sizeof(*h));
return h;
}
static void
_hash_delete(struct hashmap *hash) {
int i;
for (i=0;i<HASH_SIZE;i++) {
struct keyvalue * node = hash->node[i];
while (node) {
struct keyvalue * next = node->next;
_release_queue(node->queue);
skynet_free(node);
node = next;
}
}
skynet_free(hash);
}
///////////////
struct dummy *
dummy_create(void) {
struct dummy * d = skynet_malloc(sizeof(*d));
d->map = _hash_new();
return d;
}
void
dummy_release(struct dummy *d) {
_hash_delete(d->map);
skynet_free(d);
}
static inline void
to_bigendian(uint8_t *buffer, uint32_t n) {
buffer[0] = (n >> 24) & 0xff;
buffer[1] = (n >> 16) & 0xff;
buffer[2] = (n >> 8) & 0xff;
buffer[3] = n & 0xff;
}
static inline void
_header_to_message(const struct remote_message_header * header, uint8_t * message) {
to_bigendian(message , header->source);
to_bigendian(message+4 , header->destination);
to_bigendian(message+8 , header->session);
}
static inline uint32_t
from_bigendian(uint32_t n) {
union {
uint32_t big;
uint8_t bytes[4];
} u;
u.big = n;
return u.bytes[0] << 24 | u.bytes[1] << 16 | u.bytes[2] << 8 | u.bytes[3];
}
static inline void
_message_to_header(const uint32_t *message, struct remote_message_header *header) {
header->source = from_bigendian(message[0]);
header->destination = from_bigendian(message[1]);
header->session = from_bigendian(message[2]);
}
static void
_dispatch_queue(struct dummy *h, struct msg_queue * queue, uint32_t handle, const char name[GLOBALNAME_LENGTH] ) {
struct msg * m = _pop_queue(queue);
while (m) {
struct remote_message_header cookie;
uint8_t *ptr = m->buffer + m->size - sizeof(cookie);
memcpy(&cookie, ptr, sizeof(cookie));
int type = cookie.destination >> HANDLE_REMOTE_SHIFT;
skynet_send(h->ctx, cookie.source, handle , type | PTYPE_TAG_DONTCOPY, cookie.session, m->buffer, m->size - sizeof(cookie));
m = _pop_queue(queue);
}
}
static void
_update_name(struct dummy *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
struct keyvalue * node = _hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
}
node->value = handle;
if (node->queue) {
_dispatch_queue(h, node->queue, handle, name);
_release_queue(node->queue);
node->queue = NULL;
}
}
static void
_send_name(struct dummy *h, uint32_t source, const char name[GLOBALNAME_LENGTH], int type, int session, const char * msg, size_t sz) {
struct keyvalue * node = _hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
}
if (node->value == 0) {
if (node->queue == NULL) {
node->queue = _new_queue();
}
struct remote_message_header header;
header.source = source;
header.destination = type << HANDLE_REMOTE_SHIFT;
header.session = (uint32_t)session;
_push_queue(node->queue, msg, sz, &header);
} else {
// local message
skynet_send(h->ctx, source, node->value , type | PTYPE_TAG_DONTCOPY, session, (void *)msg, sz);
}
}
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct dummy * h = ud;
switch (type) {
case PTYPE_SYSTEM: {
// register name message
const struct remote_message *rmsg = msg;
assert (sz == sizeof(rmsg->destination));
_update_name(h, rmsg->destination.name, rmsg->destination.handle);
return 0;
}
default: {
// remote message out
const struct remote_message *rmsg = msg;
if (rmsg->destination.handle == 0) {
_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz);
} else {
// local message
skynet_send(context, source, rmsg->destination.handle , type | PTYPE_TAG_DONTCOPY, session, (void *)rmsg->message, rmsg->sz);
}
return 0;
}
}
}
int
dummy_init(struct dummy *d, struct skynet_context *ctx, const char * args) {
d->ctx = ctx;
skynet_harbor_start(ctx);
skynet_callback(ctx, d, _mainloop);
return 0;
}

View File

@@ -2,6 +2,16 @@
#include "skynet_harbor.h"
#include "skynet_socket.h"
/*
harbor listen the PTYPE_HARBOR (in text)
N name : update the global name
S fd id: connect to new harbor , we should send self_id to fd first , and then recv a id (check it), and at last send queue.
A fd id: accept new harbor , we should send self_id to fd , and then send queue.
If the fd is disconnected, send message to slave in PTYPE_TEXT. D id
If we don't known a globalname, send message to slave in PTYPE_TEXT. Q name
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
@@ -13,33 +23,12 @@
#define HASH_SIZE 4096
#define DEFAULT_QUEUE_SIZE 1024
struct msg {
uint8_t * buffer;
size_t size;
};
struct msg_queue {
int size;
int head;
int tail;
struct msg * data;
};
struct keyvalue {
struct keyvalue * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
struct msg_queue * queue;
};
struct hashmap {
struct keyvalue *node[HASH_SIZE];
};
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
/*
message type (8bits) is in destination high 8bits
harbor id (8bits) is also in that place , but remote message doesn't need harbor id.
harbor id (8bits) is also in that place , but remote message doesn't need harbor id.
*/
struct remote_message_header {
uint32_t source;
@@ -47,29 +36,63 @@ struct remote_message_header {
uint32_t session;
};
// 12 is sizeof(struct remote_message_header)
#define HEADER_COOKIE_LENGTH 12
struct harbor_msg {
struct remote_message_header header;
void * buffer;
size_t size;
};
struct harbor_msg_queue {
int size;
int head;
int tail;
struct harbor_msg * data;
};
struct keyvalue {
struct keyvalue * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
struct harbor_msg_queue * queue;
};
struct hashmap {
struct keyvalue *node[HASH_SIZE];
};
#define STATUS_WAIT 0
#define STATUS_HANDSHAKE 1
#define STATUS_HEADER 2
#define STATUS_CONTENT 3
#define STATUS_DOWN 4
struct slave {
int fd;
struct harbor_msg_queue *queue;
int status;
int length;
int read;
uint8_t size[4];
char * recv_buffer;
};
struct harbor {
struct skynet_context *ctx;
char * local_addr;
int id;
uint32_t slave;
struct hashmap * map;
int master_fd;
char * master_addr;
int remote_fd[REMOTE_MAX];
bool connected[REMOTE_MAX];
char * remote_addr[REMOTE_MAX];
struct slave s[REMOTE_MAX];
};
// hash table
static void
_push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct remote_message_header * header) {
push_queue_msg(struct harbor_msg_queue * queue, struct harbor_msg * m) {
// If there is only 1 free slot which is reserved to distinguish full/empty
// of circular buffer, expand it.
if (((queue->tail + 1) % queue->size) == queue->head) {
struct msg * new_buffer = skynet_malloc(queue->size * 2 * sizeof(struct msg));
struct harbor_msg * new_buffer = skynet_malloc(queue->size * 2 * sizeof(struct harbor_msg));
int i;
for (i=0;i<queue->size-1;i++) {
new_buffer[i] = queue->data[(i+queue->head) % queue->size];
@@ -80,51 +103,55 @@ _push_queue(struct msg_queue * queue, const void * buffer, size_t sz, struct rem
queue->tail = queue->size - 1;
queue->size *= 2;
}
struct msg * slot = &queue->data[queue->tail];
struct harbor_msg * slot = &queue->data[queue->tail];
*slot = *m;
queue->tail = (queue->tail + 1) % queue->size;
slot->buffer = skynet_malloc(sz + sizeof(*header));
memcpy(slot->buffer, buffer, sz);
memcpy(slot->buffer + sz, header, sizeof(*header));
slot->size = sz + sizeof(*header);
}
static struct msg *
_pop_queue(struct msg_queue * queue) {
static void
push_queue(struct harbor_msg_queue * queue, void * buffer, size_t sz, struct remote_message_header * header) {
struct harbor_msg m;
m.header = *header;
m.buffer = buffer;
m.size = sz;
push_queue_msg(queue, &m);
}
static struct harbor_msg *
pop_queue(struct harbor_msg_queue * queue) {
if (queue->head == queue->tail) {
return NULL;
}
struct msg * slot = &queue->data[queue->head];
struct harbor_msg * slot = &queue->data[queue->head];
queue->head = (queue->head + 1) % queue->size;
return slot;
}
static struct msg_queue *
_new_queue() {
struct msg_queue * queue = skynet_malloc(sizeof(*queue));
static struct harbor_msg_queue *
new_queue() {
struct harbor_msg_queue * queue = skynet_malloc(sizeof(*queue));
queue->size = DEFAULT_QUEUE_SIZE;
queue->head = 0;
queue->tail = 0;
queue->data = skynet_malloc(DEFAULT_QUEUE_SIZE * sizeof(struct msg));
queue->data = skynet_malloc(DEFAULT_QUEUE_SIZE * sizeof(struct harbor_msg));
return queue;
}
static void
_release_queue(struct msg_queue *queue) {
release_queue(struct harbor_msg_queue *queue) {
if (queue == NULL)
return;
struct msg * m = _pop_queue(queue);
while (m) {
struct harbor_msg * m;
while ((m=pop_queue(queue)) != NULL) {
skynet_free(m->buffer);
m = _pop_queue(queue);
}
skynet_free(queue->data);
skynet_free(queue);
}
static struct keyvalue *
_hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t*) name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue * node = hash->node[h % HASH_SIZE];
@@ -142,7 +169,7 @@ _hash_search(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
// Don't support erase name yet
static struct void
_hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
uint32_t *ptr = name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue ** ptr = &hash->node[h % HASH_SIZE];
@@ -160,7 +187,7 @@ _hash_erase(struct hashmap * hash, char name[GLOBALNAME_LENGTH) {
*/
static struct keyvalue *
_hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *)name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct keyvalue ** pkv = &hash->node[h % HASH_SIZE];
@@ -176,20 +203,20 @@ _hash_insert(struct hashmap * hash, const char name[GLOBALNAME_LENGTH]) {
}
static struct hashmap *
_hash_new() {
hash_new() {
struct hashmap * h = skynet_malloc(sizeof(struct hashmap));
memset(h,0,sizeof(*h));
return h;
}
static void
_hash_delete(struct hashmap *hash) {
hash_delete(struct hashmap *hash) {
int i;
for (i=0;i<HASH_SIZE;i++) {
struct keyvalue * node = hash->node[i];
while (node) {
struct keyvalue * next = node->next;
_release_queue(node->queue);
release_queue(node->queue);
skynet_free(node);
node = next;
}
@@ -199,64 +226,50 @@ _hash_delete(struct hashmap *hash) {
///////////////
static void
close_harbor(struct harbor *h, int id) {
struct slave *s = &h->s[id];
s->status = STATUS_DOWN;
if (s->fd) {
skynet_socket_close(h->ctx, s->fd);
}
if (s->queue) {
release_queue(s->queue);
s->queue = NULL;
}
}
static void
report_harbor_down(struct harbor *h, int id) {
char down[64];
int n = sprintf(down, "D %d",id);
skynet_send(h->ctx, 0, h->slave, PTYPE_TEXT, 0, down, n);
}
struct harbor *
harbor_create(void) {
struct harbor * h = skynet_malloc(sizeof(*h));
h->ctx = NULL;
h->id = 0;
h->master_fd = -1;
h->master_addr = NULL;
int i;
for (i=0;i<REMOTE_MAX;i++) {
h->remote_fd[i] = -1;
h->connected[i] = false;
h->remote_addr[i] = NULL;
}
h->map = _hash_new();
memset(h,0,sizeof(*h));
h->map = hash_new();
return h;
}
void
harbor_release(struct harbor *h) {
struct skynet_context *ctx = h->ctx;
if (h->master_fd >= 0) {
skynet_socket_close(ctx, h->master_fd);
}
skynet_free(h->master_addr);
skynet_free(h->local_addr);
int i;
for (i=0;i<REMOTE_MAX;i++) {
if (h->remote_fd[i] >= 0) {
skynet_socket_close(ctx, h->remote_fd[i]);
skynet_free(h->remote_addr[i]);
for (i=1;i<REMOTE_MAX;i++) {
struct slave *s = &h->s[i];
if (s->fd && s->status != STATUS_DOWN) {
close_harbor(h,i);
// don't call report_harbor_down.
// never call skynet_send during module exit, because of dead lock
}
}
_hash_delete(h->map);
hash_delete(h->map);
skynet_free(h);
}
static int
_connect_to(struct harbor *h, const char *ipaddress, bool blocking) {
char * port = strchr(ipaddress,':');
if (port==NULL) {
return -1;
}
int sz = port - ipaddress;
char tmp[sz + 1];
memcpy(tmp,ipaddress,sz);
tmp[sz] = '\0';
int portid = strtol(port+1, NULL,10);
skynet_error(h->ctx, "Harbor(%d) connect to %s:%d", h->id, tmp, portid);
if (blocking) {
return skynet_socket_block_connect(h->ctx, tmp, portid);
} else {
return skynet_socket_connect(h->ctx, tmp, portid);
}
}
static inline void
to_bigendian(uint8_t *buffer, uint32_t n) {
buffer[0] = (n >> 24) & 0xff;
@@ -266,7 +279,7 @@ to_bigendian(uint8_t *buffer, uint32_t n) {
}
static inline void
_header_to_message(const struct remote_message_header * header, uint8_t * message) {
header_to_message(const struct remote_message_header * header, uint8_t * message) {
to_bigendian(message , header->source);
to_bigendian(message+4 , header->destination);
to_bigendian(message+8 , header->session);
@@ -283,112 +296,207 @@ from_bigendian(uint32_t n) {
}
static inline void
_message_to_header(const uint32_t *message, struct remote_message_header *header) {
message_to_header(const uint32_t *message, struct remote_message_header *header) {
header->source = from_bigendian(message[0]);
header->destination = from_bigendian(message[1]);
header->session = from_bigendian(message[2]);
}
static void
_send_package(struct skynet_context *ctx, int fd, const void * buffer, size_t sz) {
uint8_t * sendbuf = skynet_malloc(sz+4);
to_bigendian(sendbuf, sz);
memcpy(sendbuf+4, buffer, sz);
// socket package
if (skynet_socket_send(ctx, fd, sendbuf, sz+4)) {
skynet_error(ctx, "Send to %d error", fd);
static void
forward_local_messsage(struct harbor *h, void *msg, int sz) {
const char * cookie = msg;
cookie += sz - HEADER_COOKIE_LENGTH;
struct remote_message_header header;
message_to_header((const uint32_t *)cookie, &header);
uint32_t destination = header.destination;
int type = (destination >> HANDLE_REMOTE_SHIFT) | PTYPE_TAG_DONTCOPY;
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_error(h->ctx, "Unknown destination :%x from :%x", destination, header.source);
}
}
static void
_send_remote(struct skynet_context * ctx, int fd, const char * buffer, size_t sz, struct remote_message_header * cookie) {
send_remote(struct skynet_context * ctx, int fd, const char * buffer, size_t sz, struct remote_message_header * cookie) {
uint32_t sz_header = sz+sizeof(*cookie);
uint8_t * sendbuf = skynet_malloc(sz_header+4);
to_bigendian(sendbuf, sz_header);
memcpy(sendbuf+4, buffer, sz);
_header_to_message(cookie, sendbuf+4+sz);
header_to_message(cookie, sendbuf+4+sz);
if (skynet_socket_send(ctx, fd, sendbuf, sz_header+4)) {
skynet_error(ctx, "Remote send to %d error", fd);
}
// ignore send error, because if the connection is broken, the mainloop will recv a message.
skynet_socket_send(ctx, fd, sendbuf, sz_header+4);
}
static void
_update_remote_address(struct harbor *h, int harbor_id, const char * ipaddr) {
if (harbor_id == h->id) {
return;
}
assert(harbor_id > 0 && harbor_id< REMOTE_MAX);
struct skynet_context * context = h->ctx;
if (h->remote_fd[harbor_id] >=0) {
skynet_socket_close(context, h->remote_fd[harbor_id]);
skynet_free(h->remote_addr[harbor_id]);
h->remote_addr[harbor_id] = NULL;
}
h->remote_fd[harbor_id] = _connect_to(h, ipaddr, false);
h->connected[harbor_id] = false;
}
static void
_dispatch_queue(struct harbor *h, struct msg_queue * queue, uint32_t handle, const char name[GLOBALNAME_LENGTH] ) {
dispatch_name_queue(struct harbor *h, struct keyvalue * node) {
struct harbor_msg_queue * queue = node->queue;
uint32_t handle = node->value;
int harbor_id = handle >> HANDLE_REMOTE_SHIFT;
assert(harbor_id != 0);
struct skynet_context * context = h->ctx;
int fd = h->remote_fd[harbor_id];
if (fd < 0) {
char tmp [GLOBALNAME_LENGTH+1];
memcpy(tmp, name , GLOBALNAME_LENGTH);
tmp[GLOBALNAME_LENGTH] = '\0';
skynet_error(context, "Drop message to %s (in harbor %d)",tmp,harbor_id);
struct slave *s = &h->s[harbor_id];
int fd = s->fd;
if (fd == 0) {
if (s->status == STATUS_DOWN) {
char tmp [GLOBALNAME_LENGTH+1];
memcpy(tmp, node->key, GLOBALNAME_LENGTH);
tmp[GLOBALNAME_LENGTH] = '\0';
skynet_error(context, "Drop message to %s (in harbor %d)",tmp,harbor_id);
} else {
if (s->queue == NULL) {
s->queue = node->queue;
node->queue = NULL;
} else {
struct harbor_msg * m;
while ((m = pop_queue(queue))!=NULL) {
push_queue_msg(s->queue, m);
}
}
}
return;
}
struct msg * m = _pop_queue(queue);
while (m) {
struct remote_message_header cookie;
uint8_t *ptr = m->buffer + m->size - sizeof(cookie);
memcpy(&cookie, ptr, sizeof(cookie));
cookie.destination |= (handle & HANDLE_MASK);
_header_to_message(&cookie, ptr);
_send_package(context, fd, m->buffer, m->size);
m = _pop_queue(queue);
struct harbor_msg * m;
while ((m = pop_queue(queue)) != NULL) {
m->header.destination |= (handle & HANDLE_MASK);
send_remote(context, fd, m->buffer, m->size, &m->header);
}
}
static void
_update_remote_name(struct harbor *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
struct keyvalue * node = _hash_search(h->map, name);
dispatch_queue(struct harbor *h, int id) {
struct slave *s = &h->s[id];
int fd = s->fd;
assert(fd != 0);
struct harbor_msg_queue *queue = s->queue;
if (queue == NULL)
return;
struct harbor_msg * m;
while ((m = pop_queue(queue)) != NULL) {
send_remote(h->ctx, fd, m->buffer, m->size, &m->header);
}
release_queue(queue);
s->queue = NULL;
}
static void
push_socket_data(struct harbor *h, const struct skynet_socket_message * message) {
assert(message->type == SKYNET_SOCKET_TYPE_DATA);
int fd = message->id;
int i;
int id = 0;
struct slave * s = NULL;
for (i=1;i<REMOTE_MAX;i++) {
if (h->s[i].fd == fd) {
s = &h->s[i];
id = i;
break;
}
}
if (s == NULL) {
skynet_free(message->buffer);
skynet_error(h->ctx, "Invalid socket fd (%d) data", fd);
return;
}
uint8_t * buffer = (uint8_t *)message->buffer;
int size = message->ud;
for (;;) {
switch(s->status) {
case STATUS_HANDSHAKE: {
// check id
uint8_t remote_id = buffer[0];
if (remote_id != id) {
skynet_error(h->ctx, "Invalid shakehand id (%d) from fd = %d , harbor = %d", id, fd, remote_id);
close_harbor(h,id);
return;
}
++buffer;
--size;
s->status = STATUS_HEADER;
dispatch_queue(h, id);
if (size == 0) {
break;
}
// go though
}
case STATUS_HEADER: {
// big endian 4 bytes length, the first one must be 0.
int need = 4 - s->read;
if (size < need) {
memcpy(s->size + s->read, buffer, size);
s->read += size;
return;
} else {
memcpy(s->size + s->read, buffer, need);
buffer += need;
size -= need;
if (s->size[0] != 0) {
skynet_error(h->ctx, "Message is too long from harbor %d", id);
close_harbor(h,id);
return;
}
s->length = s->size[1] << 16 | s->size[2] << 8 | s->size[3];
s->read = 0;
s->recv_buffer = skynet_malloc(s->length);
s->status = STATUS_CONTENT;
if (size == 0) {
return;
}
}
}
// go though
case STATUS_CONTENT: {
int need = s->length - s->read;
if (size < need) {
memcpy(s->recv_buffer + s->read, buffer, size);
s->read += size;
return;
}
memcpy(s->recv_buffer + s->read, buffer, need);
forward_local_messsage(h, s->recv_buffer, s->length);
s->length = 0;
s->read = 0;
s->recv_buffer = NULL;
size -= need;
buffer += need;
s->status = STATUS_HEADER;
if (size == 0)
return;
break;
}
default:
return;
}
}
}
static void
update_name(struct harbor *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
struct keyvalue * node = hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
node = hash_insert(h->map, name);
}
node->value = handle;
if (node->queue) {
_dispatch_queue(h, node->queue, handle, name);
_release_queue(node->queue);
dispatch_name_queue(h, node);
release_queue(node->queue);
node->queue = NULL;
}
}
static void
_request_master(struct harbor *h, const char name[GLOBALNAME_LENGTH], size_t i, uint32_t handle) {
uint8_t buffer[4+i];
to_bigendian(buffer, handle);
memcpy(buffer+4,name,i);
_send_package(h->ctx, h->master_fd, buffer, 4+i);
}
/*
update global name to master
2 bytes (size)
4 bytes (handle) (handle == 0 for request)
n bytes string (name)
*/
static int
_remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int type, int session, const char * msg, size_t sz) {
remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int type, int session, const char * msg, size_t sz) {
int harbor_id = destination >> HANDLE_REMOTE_SHIFT;
assert(harbor_id != 0);
struct skynet_context * context = h->ctx;
if (harbor_id == h->id) {
// local message
@@ -396,55 +504,117 @@ _remote_send_handle(struct harbor *h, uint32_t source, uint32_t destination, int
return 1;
}
int fd = h->remote_fd[harbor_id];
if (fd >= 0 && h->connected[harbor_id]) {
struct slave * s = &h->s[harbor_id];
if (s->fd == 0 || s->status == STATUS_HANDSHAKE) {
if (s->status == STATUS_DOWN) {
// throw an error return to source
// report the destination is dead
skynet_send(context, destination, source, PTYPE_ERROR, 0 , NULL, 0);
skynet_error(context, "Drop message to harbor %d from %x to %x (session = %d, msgsz = %d)",harbor_id, source, destination,session,(int)sz);
} else {
if (s->queue == NULL) {
s->queue = new_queue();
}
struct remote_message_header header;
header.source = source;
header.destination = (type << HANDLE_REMOTE_SHIFT) | (destination & HANDLE_MASK);
header.session = (uint32_t)session;
push_queue(s->queue, (void *)msg, sz, &header);
return 1;
}
} else {
struct remote_message_header cookie;
cookie.source = source;
cookie.destination = (destination & HANDLE_MASK) | ((uint32_t)type << HANDLE_REMOTE_SHIFT);
cookie.session = (uint32_t)session;
_send_remote(context, fd, msg,sz,&cookie);
} else {
// throw an error return to source
// report the destination is dead
skynet_send(context, destination, source, PTYPE_ERROR, 0 , NULL, 0);
skynet_error(context, "Drop message to harbor %d from %x to %x (session = %d, msgsz = %d)",harbor_id, source, destination,session,(int)sz);
send_remote(context, s->fd, msg,sz,&cookie);
}
return 0;
}
static void
_remote_register_name(struct harbor *h, const char name[GLOBALNAME_LENGTH], uint32_t handle) {
int i;
for (i=0;i<GLOBALNAME_LENGTH;i++) {
if (name[i] == '\0')
break;
}
if (handle != 0) {
_update_remote_name(h, name, handle);
}
_request_master(h, name,i,handle);
}
static int
_remote_send_name(struct harbor *h, uint32_t source, const char name[GLOBALNAME_LENGTH], int type, int session, const char * msg, size_t sz) {
struct keyvalue * node = _hash_search(h->map, name);
remote_send_name(struct harbor *h, uint32_t source, const char name[GLOBALNAME_LENGTH], int type, int session, const char * msg, size_t sz) {
struct keyvalue * node = hash_search(h->map, name);
if (node == NULL) {
node = _hash_insert(h->map, name);
node = hash_insert(h->map, name);
}
if (node->value == 0) {
if (node->queue == NULL) {
node->queue = _new_queue();
node->queue = new_queue();
}
struct remote_message_header header;
header.source = source;
header.destination = type << HANDLE_REMOTE_SHIFT;
header.session = (uint32_t)session;
_push_queue(node->queue, msg, sz, &header);
// 0 for request
_remote_register_name(h, name, 0);
push_queue(node->queue, (void *)msg, sz, &header);
char query[2+GLOBALNAME_LENGTH+1] = "Q ";
query[2+GLOBALNAME_LENGTH] = 0;
memcpy(query+2, name, GLOBALNAME_LENGTH);
skynet_send(h->ctx, 0, h->slave, PTYPE_TEXT, 0, query, strlen(query));
return 1;
} else {
return _remote_send_handle(h, source, node->value, type, session, msg, sz);
return remote_send_handle(h, source, node->value, type, session, msg, sz);
}
}
static void
handshake(struct harbor *h, int id) {
struct slave *s = &h->s[id];
uint8_t * handshake = skynet_malloc(1);
handshake[0] = (uint8_t)h->id;
skynet_socket_send(h->ctx, s->fd, handshake, 1);
}
static void
harbor_command(struct harbor * h, const char * msg, size_t sz, int session, uint32_t source) {
const char * name = msg + 2;
int s = (int)sz;
s -= 2;
switch(msg[0]) {
case 'N' : {
if (s <=0 || s>= GLOBALNAME_LENGTH) {
skynet_error(h->ctx, "Invalid global name %s", name);
return;
}
struct remote_name rn;
memset(&rn, 0, sizeof(rn));
memcpy(rn.name, name, s);
rn.handle = source;
update_name(h, rn.name, rn.handle);
break;
}
case 'S' :
case 'A' : {
char buffer[s+1];
memcpy(buffer, name, s);
buffer[s] = 0;
int fd=0, id=0;
sscanf(buffer, "%d %d",&fd,&id);
if (fd == 0 || id <= 0 || id>=REMOTE_MAX) {
skynet_error(h->ctx, "Invalid command %c %s", msg[0], buffer);
return;
}
struct slave * slave = &h->s[id];
if (slave->fd != 0) {
skynet_error(h->ctx, "Harbor %d alreay exist", id);
return;
}
slave->fd = fd;
skynet_socket_start(h->ctx, fd);
handshake(h, id);
if (msg[0] == 'S') {
slave->status = STATUS_HANDSHAKE;
} else {
slave->status = STATUS_HEADER;
dispatch_queue(h,id);
}
break;
}
default:
skynet_error(h->ctx, "Unknown command %s", msg);
return;
}
}
@@ -452,105 +622,57 @@ static int
harbor_id(struct harbor *h, int fd) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (h->remote_fd[i] == fd)
struct slave *s = &h->s[i];
if (s->fd == fd) {
return i;
}
}
return 0;
}
static void
close_harbor(struct harbor *h, int fd) {
int id = harbor_id(h,fd);
if (id == 0)
return;
skynet_error(h->ctx, "Harbor %d closed",id);
skynet_socket_close(h->ctx, fd);
h->remote_fd[id] = -1;
h->connected[id] = false;
}
static void
open_harbor(struct harbor *h, int fd) {
int id = harbor_id(h,fd);
if (id == 0)
return;
assert(h->connected[id] == false);
h->connected[id] = true;
}
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct harbor * h = ud;
switch (type) {
case PTYPE_SOCKET: {
const struct skynet_socket_message * message = msg;
switch(message->type) {
case SKYNET_SOCKET_TYPE_DATA:
push_socket_data(h, message);
skynet_free(message->buffer);
skynet_error(context, "recv invalid socket message (size=%d)", message->ud);
break;
case SKYNET_SOCKET_TYPE_ACCEPT:
skynet_error(context, "recv invalid socket accept message");
break;
case SKYNET_SOCKET_TYPE_ERROR:
case SKYNET_SOCKET_TYPE_CLOSE:
close_harbor(h, message->id);
case SKYNET_SOCKET_TYPE_CLOSE: {
int id = harbor_id(h, message->id);
if (id) {
report_harbor_down(h,id);
} else {
skynet_error(context, "Unkown fd (%d) closed", message->id);
}
break;
}
case SKYNET_SOCKET_TYPE_CONNECT:
open_harbor(h, message->id);
// fd forward to this service
break;
default:
skynet_error(context, "recv invalid socket message type %d", type);
break;
}
return 0;
}
case PTYPE_HARBOR: {
// remote message in
const char * cookie = msg;
cookie += sz - HEADER_COOKIE_LENGTH;
struct remote_message_header header;
_message_to_header((const uint32_t *)cookie, &header);
if (header.source == 0) {
if (header.destination < REMOTE_MAX) {
// 1 byte harbor id (0~255)
// update remote harbor address
char ip [sz - HEADER_COOKIE_LENGTH + 1];
memcpy(ip, msg, sz-HEADER_COOKIE_LENGTH);
ip[sz-HEADER_COOKIE_LENGTH] = '\0';
_update_remote_address(h, header.destination, ip);
} else {
// update global name
if (sz - HEADER_COOKIE_LENGTH > GLOBALNAME_LENGTH) {
char name[sz-HEADER_COOKIE_LENGTH+1];
memcpy(name, msg, sz-HEADER_COOKIE_LENGTH);
name[sz-HEADER_COOKIE_LENGTH] = '\0';
skynet_error(context, "Global name is too long %s", name);
}
_update_remote_name(h, msg, header.destination);
}
} else {
uint32_t destination = header.destination;
int type = (destination >> HANDLE_REMOTE_SHIFT) | PTYPE_TAG_DONTCOPY;
destination = (destination & HANDLE_MASK) | ((uint32_t)h->id << HANDLE_REMOTE_SHIFT);
skynet_send(context, header.source, destination, type, (int)header.session, (void *)msg, sz-HEADER_COOKIE_LENGTH);
return 1;
}
return 0;
}
case PTYPE_SYSTEM: {
// register name message
const struct remote_message *rmsg = msg;
assert (sz == sizeof(rmsg->destination));
_remote_register_name(h, rmsg->destination.name, rmsg->destination.handle);
harbor_command(h, msg,sz,session,source);
return 0;
}
default: {
// remote message out
const struct remote_message *rmsg = msg;
if (rmsg->destination.handle == 0) {
if (_remote_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz)) {
if (remote_send_name(h, source , rmsg->destination.name, type, session, rmsg->message, rmsg->sz)) {
return 0;
}
} else {
if (_remote_send_handle(h, source , rmsg->destination.handle, type, session, rmsg->message, rmsg->sz)) {
if (remote_send_handle(h, source , rmsg->destination.handle, type, session, rmsg->message, rmsg->sz)) {
return 0;
}
}
@@ -560,48 +682,19 @@ _mainloop(struct skynet_context * context, void * ud, int type, int session, uin
}
}
static void
_launch_gate(struct skynet_context * ctx, const char * local_addr) {
char tmp[128];
sprintf(tmp,"gate L ! %s %d %d 0",local_addr, PTYPE_HARBOR, REMOTE_MAX);
const char * gate_addr = skynet_command(ctx, "LAUNCH", tmp);
if (gate_addr == NULL) {
fprintf(stderr, "Harbor : launch gate failed\n");
exit(1);
}
uint32_t gate = strtoul(gate_addr+1 , NULL, 16);
if (gate == 0) {
fprintf(stderr, "Harbor : launch gate invalid %s", gate_addr);
exit(1);
}
const char * self_addr = skynet_command(ctx, "REG", NULL);
int n = sprintf(tmp,"broker %s",self_addr);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, tmp, n);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, "start", 5);
}
int
harbor_init(struct harbor *h, struct skynet_context *ctx, const char * args) {
h->ctx = ctx;
int sz = strlen(args)+1;
char master_addr[sz];
char local_addr[sz];
int harbor_id = 0;
sscanf(args,"%s %s %d",master_addr, local_addr, &harbor_id);
h->master_addr = skynet_strdup(master_addr);
h->id = harbor_id;
h->master_fd = _connect_to(h, master_addr, true);
if (h->master_fd == -1) {
fprintf(stderr, "Harbor: Connect to master failed\n");
exit(1);
uint32_t slave = 0;
sscanf(args,"%d %u", &harbor_id, &slave);
if (slave == 0) {
return 1;
}
h->id = harbor_id;
h->slave = slave;
skynet_callback(ctx, h, mainloop);
skynet_harbor_start(ctx);
h->local_addr = skynet_strdup(local_addr);
_launch_gate(ctx, local_addr);
skynet_callback(ctx, h, _mainloop);
_request_master(h, local_addr, strlen(local_addr), harbor_id);
return 0;
}

View File

@@ -1,313 +0,0 @@
#include "skynet.h"
#include "skynet_harbor.h"
#include "skynet_socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#define HASH_SIZE 4096
struct name {
struct name * next;
char key[GLOBALNAME_LENGTH];
uint32_t hash;
uint32_t value;
};
struct namemap {
struct name *node[HASH_SIZE];
};
struct master {
struct skynet_context *ctx;
int remote_fd[REMOTE_MAX];
bool connected[REMOTE_MAX];
char * remote_addr[REMOTE_MAX];
struct namemap map;
};
struct master *
master_create() {
struct master *m = skynet_malloc(sizeof(*m));
int i;
for (i=0;i<REMOTE_MAX;i++) {
m->remote_fd[i] = -1;
m->remote_addr[i] = NULL;
m->connected[i] = false;
}
memset(&m->map, 0, sizeof(m->map));
return m;
}
void
master_release(struct master * m) {
int i;
struct skynet_context *ctx = m->ctx;
for (i=0;i<REMOTE_MAX;i++) {
int fd = m->remote_fd[i];
if (fd >= 0) {
assert(ctx);
skynet_socket_close(ctx, fd);
}
skynet_free(m->remote_addr[i]);
}
for (i=0;i<HASH_SIZE;i++) {
struct name * node = m->map.node[i];
while (node) {
struct name * next = node->next;
skynet_free(node);
node = next;
}
}
skynet_free(m);
}
static struct name *
_search_name(struct master *m, char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *) name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct name * node = m->map.node[h % HASH_SIZE];
while (node) {
if (node->hash == h && strncmp(node->key, name, GLOBALNAME_LENGTH) == 0) {
return node;
}
node = node->next;
}
return NULL;
}
static struct name *
_insert_name(struct master *m, char name[GLOBALNAME_LENGTH]) {
uint32_t *ptr = (uint32_t *)name;
uint32_t h = ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
struct name **pname = &m->map.node[h % HASH_SIZE];
struct name * node = skynet_malloc(sizeof(*node));
memcpy(node->key, name, GLOBALNAME_LENGTH);
node->next = *pname;
node->hash = h;
node->value = 0;
*pname = node;
return node;
}
static void
_copy_name(char *name, const char * buffer, size_t sz) {
if (sz < GLOBALNAME_LENGTH) {
memcpy(name, buffer, sz);
memset(name+sz, 0 , GLOBALNAME_LENGTH - sz);
} else {
memcpy(name, buffer, GLOBALNAME_LENGTH);
}
}
static void
_connect_to(struct master *m, int id) {
assert(m->connected[id] == false);
struct skynet_context * ctx = m->ctx;
const char *ipaddress = m->remote_addr[id];
char * portstr = strchr(ipaddress,':');
if (portstr==NULL) {
skynet_error(ctx, "Harbor %d : address invalid (%s)",id, ipaddress);
return;
}
int sz = portstr - ipaddress;
char tmp[sz + 1];
memcpy(tmp,ipaddress,sz);
tmp[sz] = '\0';
int port = strtol(portstr+1,NULL,10);
skynet_error(ctx, "Master connect to harbor(%d) %s:%d", id, tmp, port);
m->remote_fd[id] = skynet_socket_connect(ctx, tmp, port);
}
static inline void
to_bigendian(uint8_t *buffer, uint32_t n) {
buffer[0] = (n >> 24) & 0xff;
buffer[1] = (n >> 16) & 0xff;
buffer[2] = (n >> 8) & 0xff;
buffer[3] = n & 0xff;
}
static void
_send_to(struct master *m, int id, const void * buf, int sz, uint32_t handle) {
uint8_t * buffer= (uint8_t *)skynet_malloc(4 + sz + 12);
to_bigendian(buffer, sz+12);
memcpy(buffer+4, buf, sz);
to_bigendian(buffer+4+sz, 0);
to_bigendian(buffer+4+sz+4, handle);
to_bigendian(buffer+4+sz+8, 0);
sz += 4 + 12;
if (skynet_socket_send(m->ctx, m->remote_fd[id], buffer, sz)) {
skynet_error(m->ctx, "Harbor %d : send error", id);
}
}
static void
_broadcast(struct master *m, const char *name, size_t sz, uint32_t handle) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
int fd = m->remote_fd[i];
if (fd < 0 || m->connected[i]==false)
continue;
_send_to(m, i , name, sz, handle);
}
}
static void
_request_name(struct master *m, const char * buffer, size_t sz) {
char name[GLOBALNAME_LENGTH];
_copy_name(name, buffer, sz);
struct name * n = _search_name(m, name);
if (n == NULL) {
return;
}
_broadcast(m, name, GLOBALNAME_LENGTH, n->value);
}
static void
_update_name(struct master *m, uint32_t handle, const char * buffer, size_t sz) {
char name[GLOBALNAME_LENGTH];
_copy_name(name, buffer, sz);
struct name * n = _search_name(m, name);
if (n==NULL) {
n = _insert_name(m,name);
}
n->value = handle;
_broadcast(m,name,GLOBALNAME_LENGTH, handle);
}
static void
close_harbor(struct master *m, int harbor_id) {
if (m->connected[harbor_id]) {
struct skynet_context * context = m->ctx;
skynet_socket_close(context, m->remote_fd[harbor_id]);
m->remote_fd[harbor_id] = -1;
m->connected[harbor_id] = false;
}
}
static void
_update_address(struct master *m, int harbor_id, const char * buffer, size_t sz) {
if (m->remote_fd[harbor_id] >= 0) {
close_harbor(m, harbor_id);
}
skynet_free(m->remote_addr[harbor_id]);
char * addr = skynet_malloc(sz+1);
memcpy(addr, buffer, sz);
addr[sz] = '\0';
m->remote_addr[harbor_id] = addr;
_connect_to(m, harbor_id);
}
static int
socket_id(struct master *m, int id) {
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (m->remote_fd[i] == id)
return i;
}
return 0;
}
static void
on_connected(struct master *m, int id) {
_broadcast(m, m->remote_addr[id], strlen(m->remote_addr[id]), id);
m->connected[id] = true;
int i;
for (i=1;i<REMOTE_MAX;i++) {
if (i == id)
continue;
const char * addr = m->remote_addr[i];
if (addr == NULL || m->connected[i] == false) {
continue;
}
_send_to(m, id , addr, strlen(addr), i);
}
}
static void
dispatch_socket(struct master *m, const struct skynet_socket_message *msg, int sz) {
int id = socket_id(m, msg->id);
switch(msg->type) {
case SKYNET_SOCKET_TYPE_CONNECT:
assert(id);
on_connected(m, id);
break;
case SKYNET_SOCKET_TYPE_ERROR:
skynet_error(m->ctx, "socket error on harbor %d", id);
// go though, close socket
case SKYNET_SOCKET_TYPE_CLOSE:
close_harbor(m, id);
break;
default:
skynet_error(m->ctx, "Invalid socket message type %d", msg->type);
break;
}
}
/*
update global name to master
4 bytes (handle) (handle == 0 for request)
n bytes string (name)
*/
static int
_mainloop(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
if (type == PTYPE_SOCKET) {
dispatch_socket(ud, msg, (int)sz);
return 0;
}
if (type != PTYPE_HARBOR) {
skynet_error(context, "None harbor message recv from %x (type = %d)", source, type);
return 0;
}
assert(sz >= 4);
struct master *m = ud;
const uint8_t *handlen = msg;
uint32_t handle = handlen[0]<<24 | handlen[1]<<16 | handlen[2]<<8 | handlen[3];
sz -= 4;
const char * name = msg;
name += 4;
if (handle == 0) {
_request_name(m , name, sz);
} else if (handle < REMOTE_MAX) {
_update_address(m , handle, name, sz);
} else {
_update_name(m , handle, name, sz);
}
return 0;
}
int
master_init(struct master *m, struct skynet_context *ctx, const char * args) {
char tmp[strlen(args) + 32];
sprintf(tmp,"gate L ! %s %d %d 0",args,PTYPE_HARBOR,REMOTE_MAX);
const char * gate_addr = skynet_command(ctx, "LAUNCH", tmp);
if (gate_addr == NULL) {
skynet_error(ctx, "Master : launch gate failed");
return 1;
}
uint32_t gate = strtoul(gate_addr+1, NULL, 16);
if (gate == 0) {
skynet_error(ctx, "Master : launch gate invalid %s", gate_addr);
return 1;
}
const char * self_addr = skynet_command(ctx, "REG", NULL);
int n = sprintf(tmp,"broker %s",self_addr);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, tmp, n);
skynet_send(ctx, 0, gate, PTYPE_TEXT, 0, "start", 5);
skynet_callback(ctx, m, _mainloop);
m->ctx = ctx;
return 0;
}

View File

@@ -55,7 +55,7 @@ traceback (lua_State *L) {
static void
_report_launcher_error(struct skynet_context *ctx) {
// sizeof "ERROR" == 5
skynet_sendname(ctx, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
skynet_sendname(ctx, 0, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
}
static const char *

View File

@@ -1,35 +1,43 @@
local skynet = require "skynet"
local harbor = require "skynet.harbor"
skynet.start(function()
assert(skynet.launch("logger", skynet.getenv "logger"))
local standalone = skynet.getenv "standalone"
local launcher = assert(skynet.launch("snlua","launcher"))
skynet.name(".launcher", launcher)
local harbor_id = tonumber(skynet.getenv "harbor")
if harbor_id == 0 then
assert(standalone == nil)
standalone = true
skynet.setenv("standalone", "true")
assert(skynet.launch("dummy"))
else
local master_addr = skynet.getenv "master"
local ok, slave = pcall(skynet.newservice, "cdummy")
if not ok then
skynet.abort()
end
skynet.name(".cslave", slave)
else
if standalone then
assert(skynet.launch("master", master_addr))
if not pcall(skynet.newservice,"cmaster") then
skynet.abort()
end
end
local local_addr = skynet.getenv "address"
assert(skynet.launch("harbor",master_addr, local_addr, harbor_id))
local ok, slave = pcall(skynet.newservice, "cslave")
if not ok then
skynet.abort()
end
skynet.name(".cslave", slave)
end
local launcher = assert(skynet.launch("snlua","launcher"))
skynet.name(".launcher", launcher)
if standalone then
local datacenter = assert(skynet.newservice "datacenterd")
local datacenter = skynet.newservice "datacenterd"
skynet.name("DATACENTER", datacenter)
end
assert(skynet.newservice "service_mgr")
assert(skynet.newservice(skynet.getenv "start" or "main"))
skynet.newservice "service_mgr"
pcall(skynet.newservice,skynet.getenv "start" or "main")
skynet.exit()
end)

79
service/cdummy.lua Normal file
View File

@@ -0,0 +1,79 @@
local skynet = require "skynet"
local globalname = {}
local queryname = {}
local harbor = {}
skynet.register_protocol {
name = "harbor",
id = skynet.PTYPE_HARBOR,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
local function response_name(name)
local address = globalname[name]
if queryname[name] then
local tmp = queryname[name]
queryname[name] = nil
for _,resp in ipairs(tmp) do
resp(true, address)
end
end
end
function harbor.REGISTER(name, handle)
assert(globalname[name] == nil)
globalname[name] = handle
response_name(name)
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
end
function harbor.QUERYNAME(name)
if name:byte() == 46 then -- "." , local name
skynet.ret(skynet.pack(skynet.localname(name)))
return
end
local result = globalname[name]
if result then
skynet.ret(skynet.pack(result))
return
end
local queue = queryname[name]
if queue == nil then
queue = { skynet.response() }
queryname[name] = queue
else
table.insert(queue, skynet.response())
end
end
function harbor.LINK(id)
skynet.ret()
end
function harbor.CONNECT(id)
skynet.error("Can't connect to other harbor in single node mode")
end
skynet.start(function()
local harbor_id = tonumber(skynet.getenv "harbor")
assert(harbor_id == 0)
skynet.dispatch("lua", function (session,source,command,...)
local f = assert(harbor[command])
f(...)
end)
skynet.dispatch("text", function(session,source,command)
-- ignore all the command
end)
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
end)

View File

@@ -1,11 +1,18 @@
local skynet = require "skynet"
local sc = require "socketchannel"
local socket = require "socket"
local cluster = require "cluster.c"
local cluster = require "cluster.core"
local config_name = skynet.getenv "cluster"
local node_address = {}
assert(loadfile(config_name, "t", node_address))()
local function loadconfig()
local f = assert(io.open(config_name))
local source = f:read "*a"
f:close()
assert(load(source, "@"..config_name, "t", node_address))()
end
local node_session = {}
local command = {}
@@ -30,19 +37,48 @@ end
local node_channel = setmetatable({}, { __index = open_channel })
function command.reload()
loadconfig()
skynet.ret(skynet.pack(nil))
end
function command.listen(source, addr, port)
local gate = skynet.newservice("gate")
if port == nil then
addr, port = string.match(node_address[addr], "([^:]+):(.*)$")
end
skynet.call(gate, "lua", "open", { address = addr, port = port })
skynet.ret(skynet.pack(nil))
end
function command.req(source, node, addr, msg, sz)
local function send_request(source, node, addr, msg, sz)
local request
local c = node_channel[node]
local session = node_session[node]
-- msg is a local pointer, cluster.packrequest will free it
request, node_session[node] = cluster.packrequest(addr, session , msg, sz)
skynet.ret(c:request(request, session))
return c:request(request, session)
end
function command.req(...)
local ok, msg, sz = pcall(send_request, ...)
if ok then
skynet.ret(msg, sz)
else
skynet.error(msg)
skynet.response()(false)
end
end
local proxy = {}
function command.proxy(source, node, name)
local fullname = node .. "." .. name
if proxy[fullname] == nil then
proxy[fullname] = skynet.newservice("clusterproxy", node, name)
end
skynet.ret(skynet.pack(proxy[fullname]))
end
local request_fd = {}
@@ -50,8 +86,13 @@ local request_fd = {}
function command.socket(source, subcmd, fd, msg)
if subcmd == "data" then
local addr, session, msg = cluster.unpackrequest(msg)
local msg, sz = skynet.rawcall(addr, "lua", msg)
local response = cluster.packresponse(session, msg, sz)
local ok , msg, sz = pcall(skynet.rawcall, addr, "lua", msg)
local response
if ok then
response = cluster.packresponse(session, true, msg, sz)
else
response = cluster.packresponse(session, false, msg)
end
socket.write(fd, response)
elseif subcmd == "open" then
skynet.error(string.format("socket accept from %s", msg))
@@ -62,7 +103,8 @@ function command.socket(source, subcmd, fd, msg)
end
skynet.start(function()
skynet.dispatch("lua", function(_, source, cmd, ...)
loadconfig()
skynet.dispatch("lua", function(session , source, cmd, ...)
local f = assert(command[cmd])
f(source, ...)
end)

27
service/clusterproxy.lua Normal file
View File

@@ -0,0 +1,27 @@
local skynet = require "skynet"
local cluster = require "cluster"
local node, address = ...
skynet.register_protocol {
name = "system",
id = skynet.PTYPE_SYSTEM,
unpack = function (...) return ... end,
}
local forward_map = {
[skynet.PTYPE_LUA] = skynet.PTYPE_SYSTEM,
[skynet.PTYPE_RESPONSE] = skynet.PTYPE_RESPONSE, -- don't free response message
}
skynet.forward_type( forward_map ,function()
local clusterd = skynet.uniqueservice("clusterd")
local n = tonumber(address)
if n then
address = n
end
skynet.dispatch("system", function (session, source, msg, sz)
local m,s = skynet.rawcall(clusterd, "lua", skynet.pack("req", node, address, msg, sz))
skynet.ret(skynet.rawcall(clusterd, "lua", skynet.pack("req", node, address, msg, sz)))
end)
end)

125
service/cmaster.lua Normal file
View File

@@ -0,0 +1,125 @@
local skynet = require "skynet"
local socket = require "socket"
--[[
master manage data :
1. all the slaves address : id -> ipaddr:port
2. all the global names : name -> address
master hold connections from slaves .
protocol slave->master :
package size 1 byte
type 1 byte :
'H' : HANDSHAKE, report slave id, and address.
'R' : REGISTER name address
'Q' : QUERY name
protocol master->slave:
package size 1 byte
type 1 byte :
'W' : WAIT n
'C' : CONNECT slave_id slave_address
'N' : NAME globalname address
'D' : DISCONNECT slave_id
]]
local slave_node = {}
local global_name = {}
local function read_package(fd)
local sz = socket.read(fd, 1)
assert(sz, "closed")
sz = string.byte(sz)
local content = socket.read(fd, sz)
return skynet.unpack(content)
end
local function pack_package(...)
local message = skynet.packstring(...)
local size = #message
assert(size <= 255 , "too long")
return string.char(size) .. message
end
local function report_slave(fd, slave_id, slave_addr)
local message = pack_package("C", slave_id, slave_addr)
local n = 0
for k,v in pairs(slave_node) do
if v.fd ~= 0 then
socket.write(v.fd, message)
n = n + 1
end
end
socket.write(fd, pack_package("W", n))
end
local function handshake(fd)
local t, slave_id, slave_addr = read_package(fd)
assert(t=='H', "Invalid handshake type " .. t)
assert(slave_id ~= 0 , "Invalid slave id 0")
if slave_node[slave_id] then
error(string.format("Slave %d already register on %s", slave_id, slave_node[slave_id].addr))
end
report_slave(fd, slave_id, slave_addr)
slave_node[slave_id] = {
fd = fd,
id = slave_id,
addr = slave_addr,
}
return slave_id , slave_addr
end
local function dispatch_slave(fd)
local t, name, address = read_package(fd)
if t == 'R' then
-- register name
assert(type(address)=="number", "Invalid request")
if not global_name[name] then
global_name[name] = address
end
local message = pack_package("N", name, address)
for k,v in pairs(slave_node) do
socket.write(v.fd, message)
end
elseif t == 'Q' then
-- query name
local address = global_name[name]
if address then
socket.write(fd, pack_package("N", name, address))
end
else
skynet.error("Invalid slave message type " .. t)
end
end
local function monitor_slave(slave_id, slave_address)
local fd = slave_node[slave_id].fd
skynet.error(string.format("Harbor %d (fd=%d) report %s", slave_id, fd, slave_address))
while pcall(dispatch_slave, fd) do end
skynet.error("slave " ..slave_id .. " is down")
local message = pack_package("D", slave_id)
slave_node[slave_id].fd = 0
for k,v in pairs(slave_node) do
socket.write(v.fd, message)
end
socket.close(fd)
end
skynet.start(function()
local master_addr = skynet.getenv "standalone"
skynet.error("master listen socket " .. tostring(master_addr))
local fd = socket.listen(master_addr)
socket.start(fd , function(id, addr)
skynet.error("connect from " .. addr .. " " .. id)
socket.start(id)
local ok, slave, slave_addr = pcall(handshake, id)
if ok then
skynet.fork(monitor_slave, slave, slave_addr)
else
skynet.error(string.format("disconnect fd = %d, error = %s", id, slave))
socket.close(id)
end
end)
end)

View File

@@ -7,10 +7,7 @@ local function console_main_loop()
while true do
local cmdline = socket.readline(stdin, "\n")
if cmdline ~= "" then
local handle = skynet.newservice(cmdline)
if handle == nil then
print("Launch error:",cmdline)
end
pcall(skynet.newservice,cmdline)
end
end
socket.unlock(stdin)

257
service/cslave.lua Normal file
View File

@@ -0,0 +1,257 @@
local skynet = require "skynet"
local socket = require "socket"
local table = table
local slaves = {}
local connect_queue = {}
local globalname = {}
local queryname = {}
local harbor = {}
local harbor_service
local monitor = {}
local function read_package(fd)
local sz = socket.read(fd, 1)
assert(sz, "closed")
sz = string.byte(sz)
local content = socket.read(fd, sz)
return skynet.unpack(content)
end
local function pack_package(...)
local message = skynet.packstring(...)
local size = #message
assert(size <= 255 , "too long")
return string.char(size) .. message
end
local function monitor_clear(id)
local v = monitor[id]
if v then
monitor[id] = nil
for _, v in ipairs(v) do
v(true)
end
end
end
local function connect_slave(slave_id, address)
local ok, err = pcall(function()
if slaves[slave_id] == nil then
local fd = assert(socket.open(address), "Can't connect to "..address)
skynet.error(string.format("Connect to harbor %d (fd=%d), %s", slave_id, fd, address))
slaves[slave_id] = fd
monitor_clear(slave_id)
socket.abandon(fd)
skynet.send(harbor_service, "harbor", string.format("S %d %d",fd,slave_id))
end
end)
if not ok then
skynet.error(err)
end
end
local function ready()
local queue = connect_queue
connect_queue = nil
for k,v in pairs(queue) do
connect_slave(k,v)
end
for name,address in pairs(globalname) do
skynet.redirect(harbor_service, address, "harbor", "N " .. name)
end
end
local function response_name(name)
local address = globalname[name]
if queryname[name] then
local tmp = queryname[name]
queryname[name] = nil
for _,resp in ipairs(tmp) do
resp(true, address)
end
end
end
local function monitor_master(master_fd)
while true do
local ok, t, id_name, address = pcall(read_package,master_fd)
if ok then
if t == 'C' then
if connect_queue then
connect_queue[id_name] = address
else
connect_slave(id_name, address)
end
elseif t == 'N' then
globalname[id_name] = address
response_name(id_name)
if connect_queue == nil then
skynet.redirect(harbor_service, address, "harbor", 0, "N " .. id_name)
end
elseif t == 'D' then
local fd = slaves[id_name]
slaves[id_name] = false
if fd then
monitor_clear(id_name)
socket.close(fd)
end
end
else
skynet.error("Master disconnect")
socket.close(master_fd)
break
end
end
end
local function accept_slave(fd)
socket.start(fd)
local id = socket.read(fd, 1)
if not id then
skynet.error(string.format("Connection (fd =%d) closed", fd))
socket.close(fd)
return
end
id = string.byte(id)
if slaves[id] ~= nil then
skynet.error(string.format("Slave %d exist (fd =%d)", id, fd))
socket.close(fd)
return
end
slaves[id] = fd
monitor_clear(id)
socket.abandon(fd)
skynet.error(string.format("Harbor %d connected (fd = %d)", id, fd))
skynet.send(harbor_service, "harbor", string.format("A %d %d", fd, id))
end
skynet.register_protocol {
name = "harbor",
id = skynet.PTYPE_HARBOR,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
pack = function(...) return ... end,
unpack = skynet.tostring,
}
local function monitor_harbor(master_fd)
return function(session, source, command)
local t = string.sub(command, 1, 1)
local arg = string.sub(command, 3)
if t == 'Q' then
-- query name
if globalname[arg] then
skynet.redirect(harbor_service, globalname[arg], "harbor", 0, "N " .. arg)
else
socket.write(master_fd, pack_package("Q", arg))
end
elseif t == 'D' then
-- harbor down
local id = tonumber(arg)
if slaves[id] then
monitor_clear(id)
end
slaves[id] = false
else
skynet.error("Unknown command ", command)
end
end
end
function harbor.REGISTER(fd, name, handle)
assert(globalname[name] == nil)
globalname[name] = handle
response_name(name)
socket.write(fd, pack_package("R", name, handle))
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
end
function harbor.LINK(fd, id)
if slaves[id] then
if monitor[id] == nil then
monitor[id] = {}
end
table.insert(monitor[id], skynet.response())
else
skynet.ret()
end
end
function harbor.CONNECT(fd, id)
if not slaves[id] then
if monitor[id] == nil then
monitor[id] = {}
end
table.insert(monitor[id], skynet.response())
else
skynet.ret()
end
end
function harbor.QUERYNAME(fd, name)
if name:byte() == 46 then -- "." , local name
skynet.ret(skynet.pack(skynet.localname(name)))
return
end
local result = globalname[name]
if result then
skynet.ret(skynet.pack(result))
return
end
local queue = queryname[name]
if queue == nil then
queue = { skynet.response() }
queryname[name] = queue
else
table.insert(queue, skynet.response())
end
end
skynet.start(function()
local master_addr = skynet.getenv "master"
local harbor_id = tonumber(skynet.getenv "harbor")
local slave_address = assert(skynet.getenv "address")
local slave_fd = socket.listen(slave_address)
skynet.error("slave connect to master " .. tostring(master_addr))
local master_fd = assert(socket.open(master_addr), "Can't connect to master")
skynet.dispatch("lua", function (_,_,command,...)
local f = assert(harbor[command])
f(master_fd, ...)
end)
skynet.dispatch("text", monitor_harbor(master_fd))
harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
local hs_message = pack_package("H", harbor_id, slave_address)
socket.write(master_fd, hs_message)
local t, n = read_package(master_fd)
assert(t == "W" and type(n) == "number", "slave shakehand failed")
skynet.error(string.format("Waiting for %d harbors", n))
skynet.fork(monitor_master, master_fd)
if n > 0 then
local co = coroutine.running()
socket.start(slave_fd, function(fd, addr)
skynet.error(string.format("New connection (fd = %d, %s)",fd, addr))
if pcall(accept_slave,fd) then
local s = 0
for k,v in pairs(slaves) do
s = s + 1
end
if s >= n then
skynet.wakeup(co)
end
end
end)
skynet.wait()
end
socket.close(slave_fd)
skynet.error("Shakehand ready")
skynet.fork(ready)
end)

View File

@@ -2,6 +2,8 @@ local skynet = require "skynet"
local command = {}
local database = {}
local wait_queue = {}
local mode = {}
local function query(db, key, ...)
if key == nil then
@@ -22,7 +24,7 @@ local function update(db, key, value, ...)
if select("#",...) == 0 then
local ret = db[key]
db[key] = value
return ret
return ret, value
else
if db[key] == nil then
db[key] = {}
@@ -31,13 +33,78 @@ local function update(db, key, value, ...)
end
end
local function wakeup(db, key1, key2, ...)
if key1 == nil then
return
end
local q = db[key1]
if q == nil then
return
end
if q[mode] == "queue" then
db[key1] = nil
if key2 then
-- throw error because can't wake up a branch
for _,response in ipairs(q) do
response(false)
end
else
return q
end
else
-- it's branch
return wakeup(q , key2, ...)
end
end
function command.UPDATE(...)
return update(database, ...)
local ret, value = update(database, ...)
if ret or value == nil then
return ret
end
local q = wakeup(wait_queue, ...)
if q then
for _, response in ipairs(q) do
response(true,value)
end
end
end
local function waitfor(db, key1, key2, ...)
if key2 == nil then
-- push queue
local q = db[key1]
if q == nil then
q = { [mode] = "queue" }
db[key1] = q
else
assert(q[mode] == "queue")
end
table.insert(q, skynet.response())
else
local q = db[key1]
if q == nil then
q = { [mode] = "branch" }
db[key1] = q
else
assert(q[mode] == "branch")
end
return waitfor(q, key2, ...)
end
end
skynet.start(function()
skynet.dispatch("lua", function (_, source, cmd, ...)
local f = assert(command[cmd])
skynet.ret(skynet.pack(f(...)))
skynet.dispatch("lua", function (_, _, cmd, ...)
if cmd == "WAIT" then
local ret = command.QUERY(...)
if ret then
skynet.ret(skynet.pack(ret))
else
waitfor(wait_queue, ...)
end
else
local f = assert(command[cmd])
skynet.ret(skynet.pack(f(...)))
end
end)
end)

View File

@@ -36,6 +36,7 @@ local function dump_list(print, list)
for _,v in ipairs(index) do
dump_line(print, v, list[v])
end
print("OK")
end
local function split_cmdline(cmdline)
@@ -88,10 +89,13 @@ end
skynet.start(function()
local listen_socket = socket.listen ("127.0.0.1", port)
print("Start debug console at 127.0.0.1",port)
skynet.error("Start debug console at 127.0.0.1 " .. port)
socket.start(listen_socket , function(id, addr)
local function print(...)
local t = { ... }
for k,v in ipairs(t) do
t[k] = tostring(v)
end
socket.write(id, table.concat(t,"\t"))
socket.write(id, "\n")
end
@@ -106,6 +110,7 @@ function COMMAND.help()
list = "List all the service",
stat = "Dump all stats",
info = "Info address : get service infomation",
exit = "exit address : kill a lua service",
kill = "kill address : kill service",
mem = "mem : show memory status",
gc = "gc : force every lua service do garbage collect",
@@ -113,6 +118,8 @@ function COMMAND.help()
snax = "lanuch a new snax service",
clearcache = "clear lua code cache",
service = "List unique service",
task = "task address : show service task detail",
inject = "inject address luascript.lua",
}
end
@@ -121,8 +128,8 @@ function COMMAND.clearcache()
end
function COMMAND.start(...)
local addr = skynet.newservice(...)
if addr then
local ok, addr = pcall(skynet.newservice, ...)
if ok then
return { [skynet.address(addr)] = ... }
else
return "Failed"
@@ -130,8 +137,8 @@ function COMMAND.start(...)
end
function COMMAND.snax(...)
local s = snax.newservice(...)
if s then
local ok, s = pcall(snax.newservice, ...)
if ok then
local addr = s.handle
return { [skynet.address(addr)] = ... }
else
@@ -142,3 +149,35 @@ end
function COMMAND.service()
return skynet.call("SERVICE", "lua", "LIST")
end
local function adjust_address(address)
if address:sub(1,1) ~= ":" then
address = bit32.replace( tonumber("0x" .. address), skynet.harbor(skynet.self()), 24, 8)
end
return address
end
function COMMAND.exit(address)
skynet.send(adjust_address(address), "debug", "EXIT")
end
function COMMAND.inject(address, filename)
address = adjust_address(address)
local f = io.open(filename, "rb")
if not f then
return "Can't open " .. filename
end
local source = f:read "*a"
f:close()
return skynet.call(address, "debug", "RUN", source, filename)
end
function COMMAND.task(address)
address = adjust_address(address)
return skynet.call(address,"debug","TASK")
end
function COMMAND.info(address)
address = adjust_address(address)
return skynet.call(address,"debug","INFO")
end

View File

@@ -1,84 +1,23 @@
local skynet = require "skynet"
local gateserver = require "snax.gateserver"
local netpack = require "netpack"
local socketdriver = require "socketdriver"
local socket
local queue
local watchdog
local maxclient
local client_number = 0
local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local connection = {} -- fd -> connection : { fd , client, agent , ip, mode }
local forwarding = {} -- agent -> connection
function CMD.open( source , conf )
assert(not socket)
local address = conf.address or "0.0.0.0"
local port = assert(conf.port)
maxclient = conf.maxclient or 1024
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
}
local handler = {}
function handler.open(source, conf)
watchdog = conf.watchdog or source
socket = socketdriver.listen(address, port)
socketdriver.start(socket)
end
function CMD.close()
assert(socket)
socketdriver.close(socket)
socket = nil
end
local function unforward(c)
if c.agent then
forwarding[c.agent] = nil
c.agent = nil
c.client = nil
end
end
local function start(c)
if not c.mode then
c.mode = "open"
socketdriver.start(c.fd)
end
end
function CMD.forward(source, fd, client, address)
local c = assert(connection[fd])
unforward(c)
start(c)
c.client = client or 0
c.agent = address or source
forwarding[c.agent] = c
end
function CMD.accept(source, fd)
local c = assert(connection[fd])
unforward(c)
start(c)
end
function CMD.kick(source, fd)
local c
if fd then
c = connection[fd]
else
c = forwarding[source]
end
assert(c)
if c.mode ~= "close" then
c.mode = "close"
socketdriver.close(c.fd)
end
end
local MSG = {}
function MSG.data(fd, msg, sz)
function handler.message(fd, msg, sz)
-- recv a package, forward it
local c = connection[fd]
local agent = c.agent
@@ -89,67 +28,65 @@ function MSG.data(fd, msg, sz)
end
end
function MSG.more()
for fd, msg, sz in netpack.pop, queue do
MSG.data(fd, msg, sz)
end
end
function MSG.open(fd, msg)
if client_number >= maxclient then
socketdriver.close(fd)
return
end
function handler.connect(fd, addr)
local c = {
fd = fd,
ip = msg,
ip = addr,
}
connection[fd] = c
client_number = client_number + 1
skynet.send(watchdog, "lua", "socket", "open", fd, msg)
skynet.send(watchdog, "lua", "socket", "open", fd, addr)
end
local function close_fd(fd, message)
local function unforward(c)
if c.agent then
forwarding[c.agent] = nil
c.agent = nil
c.client = nil
end
end
local function close_fd(fd)
local c = connection[fd]
if c then
unforward(c)
connection[fd] = nil
client_number = client_number - 1
end
end
function MSG.close(fd)
function handler.disconnect(fd)
close_fd(fd)
skynet.send(watchdog, "lua", "socket", "close", fd)
end
function MSG.error(fd, msg)
function handler.error(fd, msg)
close_fd(fd)
skynet.send(watchdog, "lua", "socket", "error", fd, msg)
end
skynet.register_protocol {
name = "socket",
id = skynet.PTYPE_SOCKET, -- PTYPE_SOCKET = 6
unpack = function ( msg, sz )
return netpack.filter( queue, msg, sz)
end,
dispatch = function (_, _, q, type, ...)
queue = q
if type then
MSG[type](...)
end
end
}
local CMD = {}
skynet.register_protocol {
name = "client",
id = skynet.PTYPE_CLIENT,
}
function CMD.forward(source, fd, client, address)
local c = assert(connection[fd])
unforward(c)
c.client = client or 0
c.agent = address or source
forwarding[c.agent] = c
gateserver.openclient(fd)
end
skynet.start(function()
skynet.dispatch("lua", function (_, address, cmd, ...)
local f = assert(CMD[cmd])
skynet.ret(skynet.pack(f(address, ...)))
end)
end)
function CMD.accept(source, fd)
local c = assert(connection[fd])
unforward(c)
gateserver.openclient(fd)
end
function CMD.kick(source, fd)
gateserver.closeclient(fd)
end
function handler.command(cmd, source, ...)
local f = assert(CMD[cmd])
return f(source, ...)
end
gateserver.start(handler)

View File

@@ -28,17 +28,7 @@ function command.STAT()
return list
end
function command.INFO(_, _, handle)
handle = handle_to_address(handle)
if services[handle] == nil then
return
else
local result = skynet.call(handle,"debug","INFO")
return result
end
end
function command.KILL(_, _, handle)
function command.KILL(_, handle)
handle = handle_to_address(handle)
skynet.kill(handle)
local ret = { [skynet.address(handle)] = tostring(services[handle]) }
@@ -62,18 +52,29 @@ function command.GC()
return command.MEM()
end
function command.REMOVE(_,_, handle)
function command.REMOVE(_, handle)
services[handle] = nil
local response = instance[handle]
if response then
-- instance is dead
response(false)
instance[handle] = nil
end
-- don't return (skynet.ret) because the handle may exit
return NORET
end
function command.LAUNCH(address, session, service, ...)
local function return_string(str)
return str
end
function command.LAUNCH(_, service, ...)
local param = table.concat({...}, " ")
local inst = skynet.launch(service, param)
if inst then
services[inst] = service .. " " .. param
instance[inst] = { session = session, address = address }
instance[inst] = skynet.response(return_string)
else
skynet.ret("") -- launch failed
end
@@ -83,9 +84,9 @@ end
function command.ERROR(address)
-- see serivce-src/service_lua.c
-- init failed
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, "")
local response = instance[address]
if response then
response(false)
instance[address] = nil
end
services[address] = nil
@@ -94,9 +95,9 @@ end
function command.LAUNCHOK(address)
-- init notice
local reply = instance[address]
if reply then
skynet.redirect(reply.address , 0, "response", reply.session, skynet.address(address))
local response = instance[address]
if response then
response(true, skynet.address(address))
instance[address] = nil
end
@@ -117,7 +118,7 @@ skynet.register_protocol {
else
-- launch request
local service, param = string.match(cmd,"([^ ]+) (.*)")
command.LAUNCH(address, session, service, param)
command.LAUNCH(_, service, param)
end
end,
}
@@ -126,7 +127,7 @@ skynet.dispatch("lua", function(session, address, cmd , ...)
cmd = string.upper(cmd)
local f = command[cmd]
if f then
local ret = f(address, session, ...)
local ret = f(address, ...)
if ret ~= NORET then
skynet.ret(skynet.pack(ret))
end

View File

@@ -1,5 +1,5 @@
local skynet = require "skynet"
local mc = require "multicast.c"
local mc = require "multicast.core"
local datacenter = require "datacenter"
local harbor_id = skynet.harbor(skynet.self())
@@ -50,8 +50,10 @@ function command.DEL(source, c)
channel[c] = nil
channel_n[c] = nil
channel_remote[c] = nil
for node in pairs(remote) do
skynet.send(node_address[node], "lua", "DELR", c)
if remote then
for node in pairs(remote) do
skynet.send(node_address[node], "lua", "DELR", c)
end
end
return NORET
end

View File

@@ -117,7 +117,6 @@ local function register_global()
function cmd.REPORT(m)
mgr[m] = true
skynet.watch(m)
end
local function add_list(all, m)
@@ -176,7 +175,7 @@ skynet.start(function()
end
end)
local handle = skynet.localname ".service"
if handle ~= 0 then
if handle then
skynet.error(".service is already register by ", skynet.address(handle))
skynet.exit()
else

118
service/sharedatad.lua Normal file
View File

@@ -0,0 +1,118 @@
local skynet = require "skynet"
local sharedata = require "sharedata.corelib"
local table = table
local NORET = {}
local pool = {}
local objmap = {}
local function newobj(name, tbl)
assert(pool[name] == nil)
local cobj = sharedata.host.new(tbl)
sharedata.host.incref(cobj)
local v = { value = tbl , obj = cobj, watch = {} }
objmap[cobj] = v
pool[name] = v
end
local function collectobj()
while true do
skynet.sleep(600 * 100) -- sleep 10 min
collectgarbage()
for obj, v in pairs(objmap) do
if v == true then
if sharedata.host.getref(obj) <= 0 then
objmap[obj] = nil
sharedata.host.delete(obj)
end
end
end
end
end
local CMD = {}
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)
f()
elseif dt == "nil" then
value = {}
else
error ("Unknown data type " .. dt)
end
newobj(name, value)
end
function CMD.delete(name)
local v = assert(pool[name])
pool[name] = nil
assert(objmap[v.obj])
objmap[v.obj] = true
sharedata.host.decref(v.obj)
for _,response in ipairs(v.watch) do
response(true)
end
end
function CMD.query(name)
local v = assert(pool[name])
local obj = v.obj
sharedata.host.incref(obj)
return v.obj
end
function CMD.confirm(cobj)
if objmap[cobj] then
sharedata.host.decref(cobj)
end
return NORET
end
function CMD.update(name, t)
local v = pool[name]
local watch, oldcobj
if v then
watch = v.watch
oldcobj = v.obj
objmap[oldcobj] = true
sharedata.host.decref(oldcobj)
pool[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
response(true, newobj)
end
end
end
function CMD.monitor(name, obj)
local v = assert(pool[name])
if obj ~= v.obj then
return v.obj
end
table.insert(v.watch, skynet.response())
return NORET
end
skynet.start(function()
skynet.fork(collectobj)
skynet.dispatch("lua", function (session, source ,cmd, ...)
local f = assert(CMD[cmd])
local r = f(...)
if r ~= NORET then
skynet.ret(skynet.pack(r))
end
end)
end)

View File

@@ -1,10 +1,17 @@
local skynet = require "skynet"
local c = require "skynet.c"
local c = require "skynet.core"
local snax_interface = require "snax.interface"
local profile = require "profile"
local snax = require "snax"
local func = snax_interface(tostring(...), _ENV)
local snax_name = tostring(...)
local func, pattern = snax_interface(snax_name, _ENV)
local snax_path = pattern:sub(1,pattern:find("?", 1, true)-1) .. snax_name .. "/"
package.path = snax_path .. "?.lua;" .. package.path
SERVICE_NAME = snax_name
SERVICE_PATH = snax_path
local mode
local thread_id
local message_queue = {}

View File

@@ -30,7 +30,7 @@ void skynet_error(struct skynet_context * context, const char *msg, ...);
const char * skynet_command(struct skynet_context * context, const char * cmd , const char * parm);
uint32_t skynet_queryname(struct skynet_context * context, const char * name);
int skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * msg, size_t sz);
int skynet_sendname(struct skynet_context * context, const char * destination , int type, int session, void * msg, size_t sz);
int skynet_sendname(struct skynet_context * context, uint32_t source, const char * destination , int type, int session, void * msg, size_t sz);
int skynet_isremote(struct skynet_context *, uint32_t handle, int * harbor);

View File

@@ -49,7 +49,6 @@ skynet_handle_register(struct skynet_context *ctx) {
rwlock_wunlock(&s->lock);
handle |= s->harbor;
skynet_context_init(ctx, handle);
return handle;
}
}
@@ -67,8 +66,9 @@ skynet_handle_register(struct skynet_context *ctx) {
}
}
void
int
skynet_handle_retire(uint32_t handle) {
int ret = 0;
struct handle_storage *s = H;
rwlock_wlock(&s->lock);
@@ -79,6 +79,7 @@ skynet_handle_retire(uint32_t handle) {
if (ctx != NULL && skynet_context_handle(ctx) == handle) {
skynet_context_release(ctx);
s->slot[hash] = NULL;
ret = 1;
int i;
int j=0, n=s->name_count;
for (i=0; i<n; ++i) {
@@ -94,6 +95,8 @@ skynet_handle_retire(uint32_t handle) {
}
rwlock_wunlock(&s->lock);
return ret;
}
void
@@ -105,10 +108,14 @@ skynet_handle_retireall() {
for (i=0;i<s->slot_size;i++) {
rwlock_rlock(&s->lock);
struct skynet_context * ctx = s->slot[i];
uint32_t handle = 0;
if (ctx)
handle = skynet_context_handle(ctx);
rwlock_runlock(&s->lock);
if (ctx != NULL) {
++n;
skynet_handle_retire(skynet_context_handle(ctx));
if (handle != 0) {
if (skynet_handle_retire(handle)) {
++n;
}
}
}
if (n==0)

View File

@@ -8,7 +8,7 @@
struct skynet_context;
uint32_t skynet_handle_register(struct skynet_context *);
void skynet_handle_retire(uint32_t handle);
int skynet_handle_retire(uint32_t handle);
struct skynet_context * skynet_handle_grab(uint32_t handle);
void skynet_handle_retireall();

View File

@@ -17,23 +17,6 @@ skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int session) {
skynet_context_send(REMOTE, rmsg, sizeof(*rmsg) , source, type , session);
}
void
skynet_harbor_register(struct remote_name *rname) {
if (REMOTE == NULL)
return;
int i;
int number = 1;
for (i=0;i<GLOBALNAME_LENGTH;i++) {
char c = rname->name[i];
if (!(c >= '0' && c <='9')) {
number = 0;
break;
}
}
assert(number == 0);
skynet_context_send(REMOTE, rname, sizeof(*rname), 0, PTYPE_SYSTEM , 0);
}
int
skynet_harbor_message_isremote(uint32_t handle) {
assert(HARBOR != ~0);

View File

@@ -23,7 +23,6 @@ struct remote_message {
};
void skynet_harbor_send(struct remote_message *rmsg, uint32_t source, int session);
void skynet_harbor_register(struct remote_name *rname);
int skynet_harbor_message_isremote(uint32_t handle);
void skynet_harbor_init(int harbor);
void skynet_harbor_start(void * ctx);

View File

@@ -7,6 +7,7 @@ struct skynet_config {
const char * daemon;
const char * module_path;
const char * bootstrap;
const char * logger;
};
#define THREAD_WORKER 0

View File

@@ -11,6 +11,7 @@
#include <lualib.h>
#include <lauxlib.h>
#include <signal.h>
#include <assert.h>
static int
optint(const char *key, int opt) {
@@ -51,7 +52,6 @@ optstring(const char *key,const char * opt) {
static void
_init_env(lua_State *L) {
lua_pushglobaltable(L);
lua_pushnil(L); /* first key */
while (lua_next(L, -2) != 0) {
int keyt = lua_type(L, -2);
@@ -83,11 +83,27 @@ int sigign() {
return 0;
}
static const char * load_config = "\
local config_name = ...\
local f = assert(io.open(config_name))\
local code = assert(f:read \'*a\')\
local function getenv(name) return assert(os.getenv(name), name) end\
code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\
f:close()\
local result = {}\
assert(load(code,\'=(load)\',\'t\',result))()\
return result\
";
int
main(int argc, char *argv[]) {
const char * config_file = "config";
const char * config_file = NULL ;
if (argc > 1) {
config_file = argv[1];
} else {
fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
"usage: skynet configfilename\n");
return 1;
}
skynet_globalinit();
skynet_env_init();
@@ -98,11 +114,12 @@ main(int argc, char *argv[]) {
struct lua_State *L = lua_newstate(skynet_lalloc, NULL);
luaL_openlibs(L); // link lua lib
lua_close(L);
L = luaL_newstate();
int err = luaL_dofile(L, config_file);
int err = luaL_loadstring(L, load_config);
assert(err == LUA_OK);
lua_pushstring(L, config_file);
err = lua_pcall(L, 1, 1, 0);
if (err) {
fprintf(stderr,"%s\n",lua_tostring(L,-1));
lua_close(L);
@@ -115,6 +132,7 @@ main(int argc, char *argv[]) {
config.harbor = optint("harbor", 1);
config.bootstrap = optstring("bootstrap","snlua bootstrap");
config.daemon = optstring("daemon", NULL);
config.logger = optstring("logger", NULL);
lua_close(L);

View File

@@ -132,6 +132,8 @@ skynet_context_new(const char * name, const char *param) {
ctx->init = false;
ctx->endless = false;
// Should set to 0 first to avoid skynet_handle_retireall get an uninitialized handle
ctx->handle = 0;
ctx->handle = skynet_handle_register(ctx);
struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
// init function maybe use ctx->handle, so it must init at last
@@ -230,12 +232,22 @@ _dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
size_t sz = msg->sz & HANDLE_MASK;
if (!ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz)) {
skynet_free(msg->data);
}
}
CHECKCALLING_END(ctx)
}
void
skynet_context_dispatchall(struct skynet_context * ctx) {
// for skynet_error
struct skynet_message msg;
struct message_queue *q = ctx->queue;
while (!skynet_mq_pop(q,&msg)) {
_dispatch_message(ctx, &msg);
}
}
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q) {
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
if (q == NULL) {
q = skynet_globalmq_pop();
if (q==NULL)
@@ -251,18 +263,27 @@ skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue
return skynet_globalmq_pop();
}
int i,n=1;
struct skynet_message msg;
if (skynet_mq_pop(q,&msg)) {
skynet_context_release(ctx);
return skynet_globalmq_pop();
}
skynet_monitor_trigger(sm, msg.source , handle);
for (i=0;i<n;i++) {
if (skynet_mq_pop(q,&msg)) {
skynet_context_release(ctx);
return skynet_globalmq_pop();
} else if (i==0 && weight >= 0) {
n = skynet_mq_length(q);
n >>= weight;
}
if (ctx->cb == NULL) {
skynet_free(msg.data);
} else {
_dispatch_message(ctx, &msg);
skynet_monitor_trigger(sm, msg.source , handle);
if (ctx->cb == NULL) {
skynet_free(msg.data);
} else {
_dispatch_message(ctx, &msg);
}
skynet_monitor_trigger(sm, 0,0);
}
assert(q == ctx->queue);
@@ -275,8 +296,6 @@ skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue
}
skynet_context_release(ctx);
skynet_monitor_trigger(sm, 0,0);
return q;
}
@@ -342,11 +361,7 @@ cmd_reg(struct skynet_context * context, const char * param) {
} else if (param[0] == '.') {
return skynet_handle_namehandle(context->handle, param + 1);
} else {
assert(context->handle!=0);
struct remote_name *rname = skynet_malloc(sizeof(*rname));
copy_name(rname->name, param);
rname->handle = context->handle;
skynet_harbor_register(rname);
skynet_error(context, "Can't register global name %s in C", param);
return NULL;
}
}
@@ -355,8 +370,10 @@ static const char *
cmd_query(struct skynet_context * context, const char * param) {
if (param[0] == '.') {
uint32_t handle = skynet_handle_findname(param+1);
sprintf(context->result, ":%x", handle);
return context->result;
if (handle) {
sprintf(context->result, ":%x", handle);
return context->result;
}
}
return NULL;
}
@@ -377,10 +394,7 @@ cmd_name(struct skynet_context * context, const char * param) {
if (name[0] == '.') {
return skynet_handle_namehandle(handle_id, name + 1);
} else {
struct remote_name *rname = skynet_malloc(sizeof(*rname));
copy_name(rname->name, name);
rname->handle = handle_id;
skynet_harbor_register(rname);
skynet_error(context, "Can't set global name %s in C", name);
}
return NULL;
}
@@ -559,12 +573,16 @@ _filter_args(struct skynet_context * context, int type, int *session, void ** da
*data = msg;
}
assert((*sz & HANDLE_MASK) == *sz);
*sz |= type << HANDLE_REMOTE_SHIFT;
}
int
skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
if ((sz & HANDLE_MASK) != sz) {
skynet_error(context, "The message to %x is too large (sz = %lu)", destination, sz);
skynet_free(data);
return -1;
}
_filter_args(context, type, &session, (void **)&data, &sz);
if (source == 0) {
@@ -596,8 +614,10 @@ skynet_send(struct skynet_context * context, uint32_t source, uint32_t destinati
}
int
skynet_sendname(struct skynet_context * context, const char * addr , int type, int session, void * data, size_t sz) {
uint32_t source = context->handle;
skynet_sendname(struct skynet_context * context, uint32_t source, const char * addr , int type, int session, void * data, size_t sz) {
if (source == 0) {
source = context->handle;
}
uint32_t des = 0;
if (addr[0] == ':') {
des = strtoul(addr+1, NULL, 16);
@@ -630,11 +650,6 @@ skynet_context_handle(struct skynet_context *ctx) {
return ctx->handle;
}
void
skynet_context_init(struct skynet_context *ctx, uint32_t handle) {
ctx->handle = handle;
}
void
skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
context->cb = cb;

View File

@@ -12,12 +12,12 @@ struct skynet_context * skynet_context_new(const char * name, const char * parm)
void skynet_context_grab(struct skynet_context *);
struct skynet_context * skynet_context_release(struct skynet_context *);
uint32_t skynet_context_handle(struct skynet_context *);
void skynet_context_init(struct skynet_context *, uint32_t handle);
int skynet_context_push(uint32_t handle, struct skynet_message *message);
void skynet_context_send(struct skynet_context * context, void * msg, size_t sz, uint32_t source, int type, int session);
int skynet_context_newsession(struct skynet_context *);
struct message_queue * skynet_context_message_dispatch(struct skynet_monitor *, struct message_queue *); // return next queue
struct message_queue * skynet_context_message_dispatch(struct skynet_monitor *, struct message_queue *, int weight); // return next queue
int skynet_context_total();
void skynet_context_dispatchall(struct skynet_context * context); // for skynet_error output before exit
void skynet_context_endless(uint32_t handle); // for monitor

View File

@@ -133,12 +133,6 @@ skynet_socket_connect(struct skynet_context *ctx, const char *host, int port) {
return socket_server_connect(SOCKET_SERVER, source, host, port);
}
int
skynet_socket_block_connect(struct skynet_context *ctx, const char *host, int port) {
uint32_t source = skynet_context_handle(ctx);
return socket_server_block_connect(SOCKET_SERVER, source, host, port);
}
int
skynet_socket_bind(struct skynet_context *ctx, int fd) {
uint32_t source = skynet_context_handle(ctx);
@@ -156,3 +150,8 @@ skynet_socket_start(struct skynet_context *ctx, int id) {
uint32_t source = skynet_context_handle(ctx);
socket_server_start(SOCKET_SERVER, source, id);
}
void
skynet_socket_nodelay(struct skynet_context *ctx, int id) {
socket_server_nodelay(SOCKET_SERVER, id);
}

View File

@@ -25,9 +25,9 @@ int skynet_socket_send(struct skynet_context *ctx, int id, void *buffer, int sz)
void skynet_socket_send_lowpriority(struct skynet_context *ctx, int id, void *buffer, int sz);
int skynet_socket_listen(struct skynet_context *ctx, const char *host, int port, int backlog);
int skynet_socket_connect(struct skynet_context *ctx, const char *host, int port);
int skynet_socket_block_connect(struct skynet_context *ctx, const char *host, int port);
int skynet_socket_bind(struct skynet_context *ctx, int fd);
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);
#endif

View File

@@ -27,6 +27,7 @@ struct monitor {
struct worker_parm {
struct monitor *m;
int id;
int weight;
};
#define CHECK_ABORT if (skynet_context_total()==0) break;
@@ -118,12 +119,13 @@ static void *
_worker(void *p) {
struct worker_parm *wp = p;
int id = wp->id;
int weight = wp->weight;
struct monitor *m = wp->m;
struct skynet_monitor *sm = m->m[id];
skynet_initthread(THREAD_WORKER);
struct message_queue * q = NULL;
for (;;) {
q = skynet_context_message_dispatch(sm, q);
q = skynet_context_message_dispatch(sm, q, weight);
if (q == NULL) {
CHECK_ABORT
if (pthread_mutex_lock(&m->mutex) == 0) {
@@ -169,10 +171,20 @@ _start(int thread) {
create_thread(&pid[1], _timer, m);
create_thread(&pid[2], _socket, m);
static int weight[] = {
-1, -1, -1, -1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, };
struct worker_parm wp[thread];
for (i=0;i<thread;i++) {
wp[i].m = m;
wp[i].id = i;
if (i < sizeof(weight)/sizeof(weight[0])) {
wp[i].weight= weight[i];
} else {
wp[i].weight = 0;
}
create_thread(&pid[i+3], _worker, &wp[i]);
}
@@ -184,7 +196,7 @@ _start(int thread) {
}
static void
bootstrap(const char * cmdline) {
bootstrap(struct skynet_context * logger, const char * cmdline) {
int sz = strlen(cmdline);
char name[sz+1];
char args[sz+1];
@@ -192,6 +204,7 @@ bootstrap(const char * cmdline) {
struct skynet_context *ctx = skynet_context_new(name, args);
if (ctx == NULL) {
skynet_error(NULL, "Bootstrap error : %s\n", cmdline);
skynet_context_dispatchall(logger);
exit(1);
}
}
@@ -210,7 +223,13 @@ skynet_start(struct skynet_config * config) {
skynet_timer_init();
skynet_socket_init();
bootstrap(config->bootstrap);
struct skynet_context *ctx = skynet_context_new("logger", config->logger);
if (ctx == NULL) {
fprintf(stderr, "Can't launch logger service\n");
exit(1);
}
bootstrap(ctx, config->bootstrap);
_start(config->thread);
skynet_socket_free();

View File

@@ -9,6 +9,7 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#if defined(__APPLE__)
#include <sys/time.h>
@@ -33,7 +34,7 @@ struct timer_event {
struct timer_node {
struct timer_node *next;
int expire;
uint32_t expire;
};
struct link_list {
@@ -43,11 +44,13 @@ struct link_list {
struct timer {
struct link_list near[TIME_NEAR];
struct link_list t[4][TIME_LEVEL-1];
struct link_list t[4][TIME_LEVEL];
int lock;
int time;
uint32_t time;
uint32_t current;
uint32_t starttime;
uint64_t current_point;
uint64_t origin_point;
};
static struct timer * TI = NULL;
@@ -70,21 +73,22 @@ link(struct link_list *list,struct timer_node *node) {
static void
add_node(struct timer *T,struct timer_node *node) {
int time=node->expire;
int current_time=T->time;
uint32_t time=node->expire;
uint32_t current_time=T->time;
if ((time|TIME_NEAR_MASK)==(current_time|TIME_NEAR_MASK)) {
link(&T->near[time&TIME_NEAR_MASK],node);
} else {
int i;
int mask=TIME_NEAR << TIME_LEVEL_SHIFT;
uint32_t mask=TIME_NEAR << TIME_LEVEL_SHIFT;
for (i=0;i<3;i++) {
if ((time|(mask-1))==(current_time|(mask-1))) {
break;
}
mask <<= TIME_LEVEL_SHIFT;
}
link(&T->t[i][((time>>(TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)-1],node);
link(&T->t[i][((time>>(TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node);
}
}
@@ -101,29 +105,38 @@ timer_add(struct timer *T,void *arg,size_t sz,int time) {
UNLOCK(T);
}
static void
move_list(struct timer *T, int level, int idx) {
struct timer_node *current = link_clear(&T->t[level][idx]);
while (current) {
struct timer_node *temp=current->next;
add_node(T,current);
current=temp;
}
}
static void
timer_shift(struct timer *T) {
LOCK(T);
int mask = TIME_NEAR;
int time = (++T->time) >> TIME_NEAR_SHIFT;
int i=0;
while ((T->time & (mask-1))==0) {
int idx=time & TIME_LEVEL_MASK;
if (idx!=0) {
--idx;
struct timer_node *current = link_clear(&T->t[i][idx]);
while (current) {
struct timer_node *temp=current->next;
add_node(T,current);
current=temp;
uint32_t ct = ++T->time;
if (ct == 0) {
move_list(T, 3, 0);
} else {
uint32_t time = ct >> TIME_NEAR_SHIFT;
int i=0;
while ((ct & (mask-1))==0) {
int idx=time & TIME_LEVEL_MASK;
if (idx!=0) {
move_list(T, i, idx);
break;
}
break;
mask <<= TIME_LEVEL_SHIFT;
time >>= TIME_LEVEL_SHIFT;
++i;
}
mask <<= TIME_LEVEL_SHIFT;
time >>= TIME_LEVEL_SHIFT;
++i;
}
}
UNLOCK(T);
}
@@ -185,7 +198,7 @@ timer_create_timer() {
}
for (i=0;i<4;i++) {
for (j=0;j<TIME_LEVEL-1;j++) {
for (j=0;j<TIME_LEVEL;j++) {
link_clear(&r->t[i][j]);
}
}
@@ -218,18 +231,41 @@ skynet_timeout(uint32_t handle, int time, int session) {
return session;
}
static uint32_t
_gettime(void) {
uint32_t t;
// centisecond: 1/100 second
static void
systime(uint32_t *sec, uint32_t *cs) {
#if !defined(__APPLE__)
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint32_t)(ti.tv_sec & 0xffffff) * 100;
clock_gettime(CLOCK_REALTIME, &ti);
*sec = (uint32_t)ti.tv_sec;
*cs = (uint32_t)(ti.tv_nsec / 10000000);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
*sec = tv.tv_sec;
*cs = tv.tv_usec / 10000;
#endif
}
static uint64_t
gettime() {
uint64_t t;
#if !defined(__APPLE__)
#ifdef CLOCK_MONOTONIC_RAW
#define CLOCK_TIMER CLOCK_MONOTONIC_RAW
#else
#define CLOCK_TIMER CLOCK_MONOTONIC
#endif
struct timespec ti;
clock_gettime(CLOCK_TIMER, &ti);
t = (uint64_t)ti.tv_sec * 100;
t += ti.tv_nsec / 10000000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
t = (uint32_t)(tv.tv_sec & 0xffffff) * 100;
t = (uint64_t)tv.tv_sec * 100;
t += tv.tv_usec / 10000;
#endif
return t;
@@ -237,10 +273,20 @@ _gettime(void) {
void
skynet_updatetime(void) {
uint32_t ct = _gettime();
if (ct != TI->current) {
int diff = ct>=TI->current?ct-TI->current:(0xffffff+1)*100-TI->current+ct;
TI->current = ct;
uint64_t cp = gettime();
if(cp < TI->current_point) {
skynet_error(NULL, "time diff error: change from %lld to %lld", cp, TI->current_point);
TI->current_point = cp;
} else if (cp != TI->current_point) {
uint32_t diff = (uint32_t)(cp - TI->current_point);
TI->current_point = cp;
uint32_t oc = TI->current;
TI->current += diff;
if (TI->current < oc) {
// when cs > 0xffffffff(about 497 days), time rewind
TI->starttime += 0xffffffff / 100;
}
int i;
for (i=0;i<diff;i++) {
timer_update(TI);
@@ -261,18 +307,9 @@ skynet_gettime(void) {
void
skynet_timer_init(void) {
TI = timer_create_timer();
TI->current = _gettime();
#if !defined(__APPLE__)
struct timespec ti;
clock_gettime(CLOCK_REALTIME, &ti);
uint32_t sec = (uint32_t)ti.tv_sec;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t sec = (uint32_t)tv.tv_sec;
#endif
uint32_t mono = _gettime() / 100;
TI->starttime = sec - mono;
systime(&TI->starttime, &TI->current);
uint64_t point = gettime();
TI->current_point = point;
TI->origin_point = point;
}

View File

@@ -5,6 +5,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
@@ -34,6 +35,8 @@
#define PRIORITY_HIGH 0
#define PRIORITY_LOW 1
#define HASH_ID(id) (((unsigned)id) % MAX_SOCKET)
struct write_buffer {
struct write_buffer * next;
char *ptr;
@@ -107,6 +110,12 @@ struct request_start {
uintptr_t opaque;
};
struct request_setopt {
int id;
int what;
int value;
};
struct request_package {
uint8_t header[8]; // 6 bytes dummy
union {
@@ -117,6 +126,7 @@ struct request_package {
struct request_listen listen;
struct request_bind bind;
struct request_start start;
struct request_setopt setopt;
} u;
uint8_t dummy[256];
};
@@ -144,9 +154,11 @@ reserve_id(struct socket_server *ss) {
if (id < 0) {
id = __sync_and_and_fetch(&(ss->alloc_id), 0x7fffffff);
}
struct socket *s = &ss->slot[id % MAX_SOCKET];
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID) {
if (__sync_bool_compare_and_swap(&s->type, SOCKET_TYPE_INVALID, SOCKET_TYPE_RESERVE)) {
s->id = id;
s->fd = -1;
return id;
} else {
// retry
@@ -265,7 +277,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) {
struct socket * s = &ss->slot[id % MAX_SOCKET];
struct socket * s = &ss->slot[HASH_ID(id)];
assert(s->type == SOCKET_TYPE_RESERVE);
if (add) {
@@ -287,7 +299,7 @@ new_fd(struct socket_server *ss, int id, int fd, uintptr_t opaque, bool add) {
// return -1 when connecting
static int
open_socket(struct socket_server *ss, struct request_open * request, struct socket_message *result, bool blocking) {
open_socket(struct socket_server *ss, struct request_open * request, struct socket_message *result) {
int id = request->id;
result->opaque = request->opaque;
result->id = id;
@@ -316,18 +328,14 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
continue;
}
socket_keepalive(sock);
if (!blocking) {
sp_nonblocking(sock);
}
sp_nonblocking(sock);
status = connect( sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
if ( status != 0 && errno != EINPROGRESS) {
close(sock);
sock = -1;
continue;
}
if (blocking) {
sp_nonblocking(sock);
}
sp_nonblocking(sock);
break;
}
@@ -359,7 +367,7 @@ open_socket(struct socket_server *ss, struct request_open * request, struct sock
return -1;
_failed:
freeaddrinfo( ai_list );
ss->slot[id % MAX_SOCKET].type = SOCKET_TYPE_INVALID;
ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
return SOCKET_ERROR;
}
@@ -504,7 +512,7 @@ send_buffer_empty(struct socket *s) {
static int
send_socket(struct socket_server *ss, struct request_send * request, struct socket_message *result, int priority) {
int id = request->id;
struct socket * s = &ss->slot[id % MAX_SOCKET];
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id != id
|| s->type == SOCKET_TYPE_HALFCLOSE
|| s->type == SOCKET_TYPE_PACCEPT) {
@@ -512,7 +520,7 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock
return -1;
}
assert(s->type != SOCKET_TYPE_PLISTEN && s->type != SOCKET_TYPE_LISTEN);
if (send_buffer_empty(s)) {
if (send_buffer_empty(s) && s->type == SOCKET_TYPE_CONNECTED) {
int n = write(s->fd, request->buffer, request->sz);
if (n<0) {
switch(errno) {
@@ -558,7 +566,7 @@ _failed:
result->id = id;
result->ud = 0;
result->data = NULL;
ss->slot[id % MAX_SOCKET].type = SOCKET_TYPE_INVALID;
ss->slot[HASH_ID(id)].type = SOCKET_TYPE_INVALID;
return SOCKET_ERROR;
}
@@ -566,7 +574,7 @@ _failed:
static int
close_socket(struct socket_server *ss, struct request_close *request, struct socket_message *result) {
int id = request->id;
struct socket * s = &ss->slot[id % MAX_SOCKET];
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id != id) {
result->id = id;
result->opaque = request->opaque;
@@ -614,7 +622,7 @@ start_socket(struct socket_server *ss, struct request_start *request, struct soc
result->opaque = request->opaque;
result->ud = 0;
result->data = NULL;
struct socket *s = &ss->slot[id % MAX_SOCKET];
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
return SOCKET_ERROR;
}
@@ -635,6 +643,17 @@ start_socket(struct socket_server *ss, struct request_start *request, struct soc
return -1;
}
static void
setopt_socket(struct socket_server *ss, struct request_setopt *request) {
int id = request->id;
struct socket *s = &ss->slot[HASH_ID(id)];
if (s->type == SOCKET_TYPE_INVALID || s->id !=id) {
return;
}
int v = request->value;
setsockopt(s->fd, IPPROTO_TCP, request->what, &v, sizeof(v));
}
static void
block_readpipe(int pipefd, void *buffer, int sz) {
for (;;) {
@@ -687,7 +706,7 @@ ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
case 'K':
return close_socket(ss,(struct request_close *)buffer, result);
case 'O':
return open_socket(ss, (struct request_open *)buffer, result, false);
return open_socket(ss, (struct request_open *)buffer, result);
case 'X':
result->opaque = 0;
result->id = 0;
@@ -698,6 +717,9 @@ ctrl_cmd(struct socket_server *ss, struct socket_message *result) {
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_HIGH);
case 'P':
return send_socket(ss, (struct request_send *)buffer, result, PRIORITY_LOW);
case 'T':
setopt_socket(ss, (struct request_setopt *)buffer);
return -1;
default:
fprintf(stderr, "socket-server: Unknown ctrl %c.\n",type);
return -1;
@@ -765,7 +787,9 @@ report_connect(struct socket_server *ss, struct socket *s, struct socket_message
result->opaque = s->opaque;
result->id = s->id;
result->ud = 0;
sp_write(ss->event_fd, s->fd, s, false);
if (send_buffer_empty(s)) {
sp_write(ss->event_fd, s->fd, s, false);
}
union sockaddr_all u;
socklen_t slen = sizeof(u);
if (getpeername(s->fd, &u.s, &slen) == 0) {
@@ -939,27 +963,13 @@ socket_server_connect(struct socket_server *ss, uintptr_t opaque, const char * a
return request.u.open.id;
}
int
socket_server_block_connect(struct socket_server *ss, uintptr_t opaque, const char * addr, int port) {
struct request_package request;
struct socket_message result;
open_request(ss, &request, opaque, addr, port);
int ret = open_socket(ss, &request.u.open, &result, true);
if (ret == SOCKET_OPEN) {
return result.id;
} else {
return -1;
}
}
// return -1 when error
int64_t
socket_server_send(struct socket_server *ss, int id, const void * buffer, int sz) {
struct socket * s = &ss->slot[id % MAX_SOCKET];
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
return -1;
}
assert(s->type != SOCKET_TYPE_RESERVE);
struct request_package request;
request.u.send.id = id;
@@ -972,11 +982,10 @@ socket_server_send(struct socket_server *ss, int id, const void * buffer, int sz
void
socket_server_send_lowpriority(struct socket_server *ss, int id, const void * buffer, int sz) {
struct socket * s = &ss->slot[id % MAX_SOCKET];
struct socket * s = &ss->slot[HASH_ID(id)];
if (s->id != id || s->type == SOCKET_TYPE_INVALID) {
return;
}
assert(s->type != SOCKET_TYPE_RESERVE);
struct request_package request;
request.u.send.id = id;
@@ -1068,4 +1077,11 @@ socket_server_start(struct socket_server *ss, uintptr_t opaque, int id) {
send_request(ss, &request, 'S', sizeof(request.u.start));
}
void
socket_server_nodelay(struct socket_server *ss, int id) {
struct request_package request;
request.u.setopt.id = id;
request.u.setopt.what = TCP_NODELAY;
request.u.setopt.value = 1;
send_request(ss, &request, 'T', sizeof(request.u.setopt));
}

View File

@@ -36,6 +36,6 @@ 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);
int socket_server_block_connect(struct socket_server *, uintptr_t opaque, const char * addr, int port);
void socket_server_nodelay(struct socket_server *, int id);
#endif

View File

@@ -1,4 +1,5 @@
local skynet = require "skynet"
local queue = require "skynet.queue"
local i = 0
local hello = "hello"
@@ -8,9 +9,27 @@ function response.ping(hello)
return hello
end
-- response.sleep and accept.hello share one lock
local lock
function accept.sleep(queue, n)
if queue then
lock(
function()
print("queue=",queue, n)
skynet.sleep(n)
end)
else
print("queue=",queue, n)
skynet.sleep(n)
end
end
function accept.hello()
lock(function()
i = i + 1
print (i, hello)
end)
end
function response.error()
@@ -19,6 +38,8 @@ end
function init( ... )
print ("ping server start:", ...)
-- init queue
lock = queue()
-- You can return "queue" for queue service mode
-- return "queue"

View File

@@ -1,10 +0,0 @@
local skynet = require "skynet"
skynet.start(function()
local ping = skynet.newservice("pingserver")
skynet.timeout(0,function()
print(skynet.call(ping,"lua","PING","ping"))
end)
print(skynet.blockcall(ping,"lua","HELLO"))
end)

23
test/testdatacenter.lua Normal file
View File

@@ -0,0 +1,23 @@
local skynet = require "skynet"
local datacenter = require "datacenter"
local function f1()
print("====1==== wait hello")
print("\t1>",datacenter.wait ("hello"))
print("====1==== wait key.foobar")
print("\t1>", pcall(datacenter.wait,"key")) -- will failed, because "key" is a branch
print("\t1>",datacenter.wait ("key", "foobar"))
end
local function f2()
skynet.sleep(10)
print("====2==== set key.foobar")
datacenter.set("key", "foobar", "bingo")
end
skynet.start(function()
datacenter.set("hello", "world")
print(datacenter.get "hello")
skynet.fork(f1)
skynet.fork(f2)
end)

View File

@@ -11,17 +11,27 @@ skynet.start(function()
end)
end)
elseif mode == "dead" then
skynet.start(function()
skynet.dispatch("lua", function (...)
skynet.sleep(100)
print("return", skynet.ret "")
end)
end)
else
skynet.start(function()
local test = skynet.newservice("testdeadcall", "test") -- launch self in test mode
local test = skynet.newservice(SERVICE_NAME, "test") -- launch self in test mode
print(pcall(function()
skynet.send(test,"lua", "hello world")
skynet.send(test,"lua", "never get there")
skynet.call(test,"lua", "fake call")
skynet.call(test,"lua", "dead call")
end))
skynet.exit()
local dead = skynet.newservice(SERVICE_NAME, "dead") -- launch self in dead mode
skynet.timeout(0, skynet.exit) -- exit after a while, so the call never return
skynet.call(dead, "lua", "whould not return")
end)
end
end

44
test/testecho.lua Normal file
View File

@@ -0,0 +1,44 @@
local skynet = require "skynet"
local mode = ...
if mode == "slave" then
skynet.start(function()
skynet.dispatch("lua", function(_,_, ...)
skynet.ret(skynet.pack(...))
end)
end)
else
skynet.start(function()
local slave = skynet.newservice(SERVICE_NAME, "slave")
local n = 100000
local start = skynet.now()
print("call salve", n, "times in queue")
for i=1,n do
skynet.call(slave, "lua")
end
print("qps = ", n/ (skynet.now() - start) * 100)
start = skynet.now()
local worker = 10
local task = n/worker
print("call salve", n, "times in parallel, worker = ", worker)
for i=1,worker do
skynet.fork(function()
for i=1,task do
skynet.call(slave, "lua")
end
worker = worker -1
if worker == 0 then
print("qps = ", n/ (skynet.now() - start) * 100)
end
end)
end
end)
end

12
test/testharborlink.lua Normal file
View File

@@ -0,0 +1,12 @@
local skynet = require "skynet"
local harbor = require "skynet.harbor"
skynet.start(function()
print("wait for harbor 2")
print("run skynet examples/config_log please")
harbor.connect(2)
print("harbor 2 connected")
print("LOG =", skynet.address(harbor.queryname "LOG"))
harbor.link(2)
print("disconnected")
end)

16
test/testhttp.lua Normal file
View File

@@ -0,0 +1,16 @@
local skynet = require "skynet"
local httpc = require "http.httpc"
skynet.start(function()
print("GET baidu.com")
local header = {}
local status, body = httpc.get("baidu.com", "/", header)
print("[header] =====>")
for k,v in pairs(header) do
print(k,v)
end
print("[body] =====>", status)
print(body)
skynet.exit()
end)

Some files were not shown because too many files have changed in this diff Show More