LuaJIT Tcp ConnectionsPosted: April 9, 2012
And finally, I come to the point where I can make basic TCP connections…
I started with IN_ADDR, SOCKADDR_IN, IN6_ADDR, SOCKADDR_IN6, SOCKADDR_STORAGE, etc. Then you have to go crawling all over the Windows SDK to collect the various bits and pieces from the following headers and more: ws2tcpip.h, ws2def.h, winsock2.h, wintypes.h, winbase.h, and a couple others.
Well, at the end of the day, you can create a simple daytime client like this:
require "DaytimeClient" local dtc = DaytimeClient("192.168.1.6") print("client ready to run") dtc:Run()
Of course, that’s a cheat, because the actual work is in the DaytimeClient class. The only thing this will do is get a date from the service, and print it out. The DaytimeClient itself is fairly straight forward though:
require "BanateCore" require "TcpSocket" class.DaytimeClient() local daytimeport = 13 function DaytimeClient:_init(hostname, port) hostname = hostname or "localhost" port = port or daytimeport self.Socket = CreateTcpClientSocket(hostname, port) self.Socket:Connect() end function DaytimeClient:Run() local buff = ffi.new("char ") local bufflen = 256 n = self.Socket:Read(buff, bufflen) while (n > 0) do buff[n] = 0 -- null terminated print(ffi.string(buff)) n = self.Socket:Read(buff, bufflen) end end
I think that’s fairly easy. To create a client of any kind, just tell which hostname and port number to connect to, call connect, then start reading/writing, depending on your protocol.
If you want to create some sort of “server” the code is equally easy:
require "BanateCore" require "TcpSocket" class.DaytimeServer() local daytimeport = 13 function DaytimeServer:_init(hostname, port) port = port or daytimeport self.Socket = CreateTcpSocket() self.Socket:BindToPort(port) self.Socket:StartListening(5) end function DaytimeServer:Run() local buff = ffi.new("char ") local bufflen = 256 print("Daytime Server Running") while (true) do local acceptedsock = self.Socket:Accept() -- Get the current date copied into a buffer local dt = os.date("%c") local dtlen = string.len(dt) memcpy(buff, dt, dtlen) buff[dtlen] = 0 -- Write the buffer to the client acceptedsock:Write(buff, dtlen) print("wrote back to client") -- close down the socket acceptedsock:Close() end end
These are just building blocks though. Now, I want to have a HTTPClient of course, and FTP, etc.
So, what’s amazing about this to me? Well, there’s no “C” code in here at all. It was all done with LuaJIT FFI interop. I wrapped up all the Winsock calls that I need, and that’s the end of that. The same can be done to port to Linux or Mac, which is nice to me because I don’t need to learn any other build systems beyond what I do with Lua. That’s a huge relief, as I often find the most difficult part of using various libraries with Lua is that even though the basic C code might be ‘portable’, the build systems associated with them are not particularly easy to use.
Why not just use LuaSocket? Well, LuaSockets is a great package, and has been quite the workhorse for several Lua based networking projects. So, nothing bad to say about it. My requirements are the following though: The code must not rely on any external build systems, it must support I/O completion ports on Windows for async communications. The number of traversals through the interop layer must be as minimal as possible. I think the biggest challenge for my needs is the io completion port thing. I actually don’t even know if LuaSockets support that, but if it doesn’t already, it’s quite a lot of work to add the pattern.
At any rate, now I have the core networking. I can now slowly stitch together the async iocompletion port versions of the same, and we’ll see whether Lua will be capable of creating services that are on par with what can be created with Node.js. I’m assuming the answer is yes, and possibly even better, so that will be interesting to find out.