-
Notifications
You must be signed in to change notification settings - Fork 16
/
net2.lua
175 lines (161 loc) · 3.98 KB
/
net2.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
------------------------------------------------------------------------------
-- NET client module
--
-- LICENSE: http://opensource.org/licenses/MIT
-- Vladimir Dronnikov <[email protected]>
--
-- Example:
-- require("net2")
-- c = net2.connect("192.168.1.1", 1234)
-- c:on("data", function(self, data) print("reply", data) end)
-- c:send("command!")
------------------------------------------------------------------------------
net2 = { }
local _meta = {
-- send data
send = function(self, s)
-- push to output queue tail
self.queue[#self.queue + 1] = s
-- and initiate sending helper unless it is locked
if not self.sending then
self.sending = true
self:_send()
end
end,
-- sending helper for draining output queue
_send = function(self)
-- queue not empty?
if #self.queue > 0 then
-- socket ready?
if self.fd and not self.connecting then
-- gently send from queue head
-- NB: queue head will be removed in sent callback
-- TODO: chunking?
local ok, err = pcall(self.fd.send, self.fd, self.queue[1])
-- send failed?
if not ok then
-- report error
self:emit("error", err)
end
end
-- queue empty
else
-- unlock sender
self.sending = false
-- report queue drained
self:emit("drain")
end
end,
-- reset output queue
flush = function(self)
self.queue = { }
end,
-- connecting helper
_reconnect = function(self)
-- create native socket
local fd = net.createConnection(self.proto or net.TCP, self.secure or 0)
-- connected ok?
fd:on("connection", function(fd)
-- store socket
self.fd = fd
-- report data received
fd:on("receive", function(fd, s)
self:emit("data", s)
end)
-- on data sent restart sending helper
fd:on("sent", function(fd)
-- remove queue head
table.remove(self.queue, 1)
self:_send()
end)
-- report connected ok
self.connecting = false
self:emit("connect")
-- restart sending helper
-- NB: it should send data which user might have send while socket was offline
self:_send()
end)
-- disconnected?
fd:on("disconnection", function(fd)
-- close native socket, just in case
fd:close()
-- release socket
self.fd = nil
-- report socket dead
self:emit("end")
-- disconnected unexpectedly?
if self.port then
-- respawn connecting helper
tmr.alarm(6, 1000, 0, function()
self:_reconnect()
end)
-- disconnected ok
else
-- report close
self:emit("close")
end
end)
-- try to connect native socket
self.connecting = true
fd:connect(self.port, self.host)
end,
-- connect to peer
connect = function(self, host, port)
self.host = assert(host, "no host")
self.port = assert(port, "no port")
self:_reconnect()
end,
-- close connection
close = function(self)
-- mark we do not want to reconnect
self.port = nil
-- close native socket
if self.fd then
self.fd:close()
end
end,
-- attach event handler
on = function(self, event, handler)
-- TODO: multiple?
self._handlers[event] = handler
end,
-- emit event
emit = function(self, event, ...)
local handler = self._handlers[event]
-- TODO: multiple?
if handler then
handler(self, ...)
end
end,
}
function net2:new()
local self = setmetatable({
_handlers = { },
}, { __index = _meta })
self:flush()
return self
end
--
-- Create TCP connection to host:port
--
function net2:tcp()
local self = net2:new()
return self
end
--
-- Create UDP connection to host:port
--
function net2:udp()
local self = net2:new()
self.proto = net.UDP
return self
end
--
-- Create TCP connection to host:port
--
function net2.connect(host, port, on_connect)
local self = net2:tcp()
self:on("connect", on_connect)
self:connect(host, port)
return self
end