-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathSocketUtils.lua
279 lines (213 loc) · 5.52 KB
/
SocketUtils.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
local ffi = require "ffi"
local bit = require "bit"
local band = bit.band
local WinSock = require "WinSock_Utils"
local SocketType = WinSock.FFI.SocketType;
local Family = WinSock.FFI.Family;
local NativeSocket = require "NativeSocket"
-- pass in a sockaddr
-- get out a more specific sockaddr_in or sockaddr_in6
function newSocketAddress(name, namelen)
local sockaddrptr = ffi.cast("struct sockaddr *", name)
local newone
if sockaddrptr.sa_family == Family.AF_INET then
newone = sockaddr_in()
elseif sockaddrptr.sa_family == Family.AF_INET6 then
newone = sockaddr_in6()
end
ffi.copy(newone, sockaddrptr, namelen)
return newone
end
local function host_serv(hostname, servicename, family, sockttype, isnumericstring)
hostname = hostname or "localhost"
family = family or Family.AF_UNSPEC;
socktype = socktype or SocketType.SOCK_STREAM;
local err;
local hints = WinSock.addrinfo();
local res = ffi.new("PADDRINFOA[1]")
--hints.ai_flags = AI_CANONNAME; -- return canonical name
hints.ai_family = family;
hints.ai_socktype = socktype;
if isnumericstring then
hints.ai_flags = AI_NUMERICHOST
end
err = WinSock.Lib.getaddrinfo(hostname, servicename, hints, res)
--print("host_serv, err: ", err);
if err ~= 0 then
-- error condition
return nil, err
end
return res[0]
end
function CreateIPV4WildcardAddress(family, port)
local inetaddr = sockaddr_in()
inetaddr.sin_family = family;
inetaddr.sin_addr.S_addr = WinSock.Lib.htonl(INADDR_ANY);
inetaddr.sin_port = WinSock.Lib.htons(port);
return inetaddr
end
function CreateSocketAddress(hostname, port, family, socktype)
family = family or Family.AF_INET
socktype = socktype or SocketType.SOCK_STREAM
--print("CreateSocketAddress(): ", hostname, port);
local hostportoffset = hostname:find(':')
if hostportoffset then
port = tonumber(hostname:sub(hostportoffset+1))
hostname = hostname:sub(1,hostportoffset-1)
print("CreateSocketAddress() - Modified: ", hostname, port)
end
local addressinfo, err = host_serv(hostname, nil, family, socktype)
if not addressinfo then
return nil, err
end
-- clone one of the addresses
local oneaddress = newSocketAddress(addressinfo.ai_addr, addressinfo.ai_addrlen)
oneaddress:SetPort(port)
-- free the addrinfos structure
err = WinSock.Lib.freeaddrinfo(addressinfo)
return oneaddress;
end
local function ReadChunk(sock, buff, size)
local nread, err = sock:Receive(buff, size)
return nread, err
end
local function ReadN(sock, buff, size)
local nleft = size;
local nread = 0;
local err
local ptr = buff
while nleft > 0 do
nread, err = sock:Receive(ptr, nleft)
--coroutine.yield();
if nread then
if nread == 0 then
break
end
nleft = nleft - nread
ptr = ptr + nread
elseif err and err ~= WSAEWOULDBLOCK then
break
end
end
local bytesread = size - nleft
if bytesread == 0 then
return nil, "eof"
end
return bytesread
end
local function WriteN(sock, buff, size)
local nleft = size;
local nwritten = 0;
local err
local ptr = ffi.cast("const uint8_t *", buff)
while nleft > 0 do
nwritten, err = sock:Send(ptr, nleft)
if not nwritten then
if err ~= WSAEWOULDBLOCK then
return nil, err
end
err = nil
else
if nwritten == 0 then
break
end
nleft = nleft - nwritten
ptr = ptr + nwritten
end
end
return size - nleft
end
local CR = string.byte("\r")
local LF = string.byte("\n")
local function ReadLine(sock, buff, maxlen)
--print("ReadLine(), Begin: ", maxlen)
assert(buff)
local nchars = 0;
local ptr = buff
local err
local bytesread
for n=1, maxlen do
bytesread, err = ReadN(sock, ptr, 1)
if not bytesread then
--print("-- ReadLine(), Error: ", err);
if err ~= "wouldblock" then
break
end
end
if ptr[0] == LF then
break
elseif ptr[0] ~= CR then
ptr = ptr + 1
nchars = nchars+1
end
end
if err and err ~= "eof" then
return nil, err
end
if nchars == 0 then
return nil, "eof"
end
return nchars
end
--[[
Helper Functions
--]]
function CreateTcpServerSocket(params)
params = params or {port = 80, backlog = 15, nonblocking=false, nodelay = false}
params.backlog = params.backlog or 15
params.port = params.port or 80
local sock, err = NativeSocket()
if not sock then
return nil, err
end
local success
success, err = sock:SetNoDelay(params.nodelay)
success, err = sock:SetReuseAddress(true);
local addr = WinSock.sockaddr_in(params.port);
local addrlen = ffi.sizeof("struct sockaddr_in")
success, err = sock:Bind(addr,addrlen)
if not success then
return nil, err
end
success, err = sock:MakePassive(params.backlog)
if not success then
return nil, err
end
success, err = sock:SetNonBlocking(params.nonblocking);
if not success then
print("SetNonBlocking: ", err);
end
return sock
end
function CreateTcpClientSocket(hostname, port)
--print("CreateTcpClientSocket: ", hostname, port)
local addr, err = CreateSocketAddress(hostname, port)
if not addr then
print("-- CreateTcpClientSocket() - could not create address: ", hostname, port)
return nil, err
end
--print("CreateTcpClientSocket(): ", addr);
local sock
sock, err = NativeSocket();
if not sock then
return nil, err
end
-- Disable delay by default on all sockets
err = sock:SetNoDelay(true)
-- Connect to the host
local success
success, err = sock:ConnectTo(addr)
if not success then
return nil, err
end
return sock
end
return {
ReadByte = ReadByte,
ReadLine = ReadLine,
ReadN = ReadN,
WriteN = WriteN,
host_serv = host_serv,
CreateTcpServerSocket = CreateTcpServerSocket,
CreateTcpClientSocket = CreateTcpClientSocket,
}