-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsocket.lua
More file actions
133 lines (121 loc) · 4.44 KB
/
socket.lua
File metadata and controls
133 lines (121 loc) · 4.44 KB
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
local utils = require("utils")
local internet = require("component").internet
local Socket = utils.makeClass(function(self, lenPattern, maxRetries)
self.lenPattern = lenPattern or ">I4"
self.maxRetries = maxRetries or 3
self.readBuffer = ""
self.currentMsgLen = nil
end)
function Socket:connect(addr, port)
self.readBuffer = ""
self.currentMsgLen = nil
if self.socket then
self.socket.close()
end
local retries = 0
while retries <= self.maxRetries do
local socket, reason = internet.connect(addr, port)
if not socket then
log("Failed to create socket: %s", reason)
self.socket = nil
return false, reason
end
-- ensure connection
local ok, reason = socket.finishConnect()
-- can get either true when connection was successful, false, or nil + reason when it failed
while not ok and not reason do
ok, reason = socket.finishConnect()
end
if ok then
-- socket connected
log("Connected to %s:%s", addr, port)
self.socket = socket
return true
else
log("Failed to connect to %s:%s, reason: %s", addr, port, reason)
retries = retries + 1
end
end
log("Retries to connect to %s:%s failed", addr, port)
-- return failure
self.socket = nil
return false, "Couldn't connect to the remote server"
end
function Socket:close(...)
return self.socket.close(...)
end
function Socket:id(...)
return self.socket.id(...)
end
function Socket:read(bytes)
if bytes then
local fullMsg = {}
while bytes > 0 do
local msg, reason = self.socket.read(bytes)
if msg then
bytes = bytes - #msg
fullMsg[#fullMsg+1] = msg
log("Read %d bytes, %d bytes left to read", #msg, bytes)
else
-- socket error
log("Socket read failed: %s", reason)
return false, reason
end
end
return true, table.concat(fullMsg)
else
return true, self.socket.read()
end
end
-- reads new data from the socket and returns all messages prepended with a length contained in the read buffer after reading
function Socket:readLenPrep()
log("internet_ready handler called")
local partialMsg, reason = self.socket.read()
if partialMsg then
self.readBuffer = self.readBuffer .. partialMsg
else
log("Socket read failed: %s", reason)
return false, reason
end
-- first stage - parse message length
local lenSize = string.packsize(self.lenPattern)
if #self.readBuffer >= lenSize and not self.currentMsgLen then
self.currentMsgLen = string.unpack(self.lenPattern, string.sub(self.readBuffer, 1, lenSize))
log("Received message length: %d", self.currentMsgLen)
end
-- second stage - while there are whole messages in buffer, dispatch message for processing and then remove it from buffer
log("Current buffer length: %d", #self.readBuffer)
local messages = {}
while self.currentMsgLen and #self.readBuffer - lenSize >= self.currentMsgLen do
messages[#messages+1] = string.sub(self.readBuffer, lenSize+1, lenSize+self.currentMsgLen)
self.readBuffer = string.sub(self.readBuffer, lenSize+self.currentMsgLen+1)
-- read next message length if available
if #self.readBuffer >= lenSize then
self.currentMsgLen = string.unpack(self.lenPattern, string.sub(self.readBuffer, 1, lenSize))
else
self.currentMsgLen = nil
end
end
return true, messages
end
-- synchronous socket write, returns status and failure reason if status is false
function Socket:write(msg)
local written = 0
-- sock.write should always write 0 bytes or the whole message, but we do a standard write loop just in case
while written < #msg do
local bytes, reason = self.socket.write(string.sub(msg, written+1)) -- correct string start for lua indexing starting at 1
if bytes then
written = written + bytes
log("Wrote %d bytes, fully written %d bytes", bytes, written)
else
-- socket error
log("Socket write failed: %s", reason)
return false, reason
end
end
return true
end
function Socket:writeLenPrep(msg)
return self:write(string.pack(self.lenPattern, #msg) .. msg)
end
return Socket