This tutorial is meant to provide users who have already some understanding in programming with the knowledge necessary to use the LUA SocketLib and the Coroutines in order to set up a TCP/IP server in a host application.
When listening to a port, the TCP server creates a half-socket (server socket). When receiving a connection the server duplicates this half-socket and connects it with the distant half-socket (client socket) in order to build the communication socket that will insure the information data flow. Therefore, at each moment, the server has an available half-socket.
The function socket.tcp() allow us to create a TCP server object. Then one can access the server functions. All the functions used after the creation of the server object are really well known for this kind of job: setoption(), bind() and listen().
Most of the SocketLib functions return one parameter if successful and two parameters if not. If a failure occurs, the first returned value is nil and the second value is the error message.
The function createTCPServer() must be called once during the initialization of the host program. Moreover, if you set it with "localhost", the TCP server will accept only localhost incoming connections (i.e the client is localized on the same computer than the server). So if you wish to be able to connect to your server from a client placed physically in another computer, you have to set host as follow: host = "*"
Creating a TCP server is the easiest part. Now, let us tackle the hardcore one: the acceptation and the management of the in going connections.
The answer to this problem is to transfer this non-ending loop into an execution path separated from the main execution path of the host program. The first function we have to write is a function that parses an URL into a key-value list. Then commands or values sent by the javascript client can be handled in a easier way. Note that we will receive data in a URL fashion.
Due to some bugs related to how Android handles URLs in Honeycomb and ICS, I decided to pass parameters using JASON, eg. we can't simply do
Hence, we can also send parameters without using a TCP server. Initially, I was using this function together with the next piece of code and a web popup URL listener to handle the communication between lua and javascript. But the approach doesn't work with all devices.
Now we write a function to initialize the TCP server, create a web popup and intercept client requests. The next piece of code illustrates how to do it.
Then we simply use an AJAX call to request or send data. After sending the data through the TCP server, we call the function update to make values available.
One can obtain the full source code here
[2] Jerome Guinot. The LUA SocketLib and the Coroutines. ozone3d.net, 2006.
Introduction
TCP/IP is a large collection of different communication protocols based upon the two original protocols: TCP and IP. TCP is at the fourth level of the OSI model (transport layer) and IP at the third level (network layer). The TCP protocol is used for the data transmission from an application to the network and can handle a byte flow (stream) between a source and an addressee in a total reliable way. A TCP connection is represented by a quintuplet {protocol, IP local_address, local_port, IP remote_address, remote_port} and is unique all over a given network. A connection is totally defined by two half-sockets: the source (client) half socket and the destination (server) half socket [2].When listening to a port, the TCP server creates a half-socket (server socket). When receiving a connection the server duplicates this half-socket and connects it with the distant half-socket (client socket) in order to build the communication socket that will insure the information data flow. Therefore, at each moment, the server has an available half-socket.
1 - Setting up a TCP/IP server
The usual procedure to create a TCP server can directly be applied with SocketLib functions. This schema is the following one:- Create a server half-socket
- Attach the half-socket to a port
- Listen on this port
local function createTCPServer( port )
local socket = require("socket")
-- Create Socket
local tcpServerSocket , err = socket.tcp()
local backlog = 5
-- Check Socket
if tcpServerSocket == nil then
return nil , err
end
-- Allow Address Reuse
tcpServerSocket:setoption( "reuseaddr" , true )
-- Bind Socket
local res, err = tcpServerSocket:bind( "*" , port )
if res == nil then
return nil , err
end
-- Check Connection
res , err = tcpServerSocket:listen( backlog )
if res == nil then
return nil , err
end
-- Return Server
return tcpServerSocket
end
The function socket.tcp() allow us to create a TCP server object. Then one can access the server functions. All the functions used after the creation of the server object are really well known for this kind of job: setoption(), bind() and listen().
Most of the SocketLib functions return one parameter if successful and two parameters if not. If a failure occurs, the first returned value is nil and the second value is the error message.
The function createTCPServer() must be called once during the initialization of the host program. Moreover, if you set it with "localhost", the TCP server will accept only localhost incoming connections (i.e the client is localized on the same computer than the server). So if you wish to be able to connect to your server from a client placed physically in another computer, you have to set host as follow: host = "*"
Creating a TCP server is the easiest part. Now, let us tackle the hardcore one: the acceptation and the management of the in going connections.
2 - Lua: Incoming and outgoing connections management
The guiding principle of managing incoming connections is to set the TCP server in a non-ending loop and to wait for the connections at the beginning of the loop with the server:accept() function. But we immediately see the problem: if we enter a non-ending loop, all the host program will freeze at that point. This is absolutely unacceptable!The answer to this problem is to transfer this non-ending loop into an execution path separated from the main execution path of the host program. The first function we have to write is a function that parses an URL into a key-value list. Then commands or values sent by the javascript client can be handled in a easier way. Note that we will receive data in a URL fashion.
function getArgs( query )
local parsed = {}
local pos = 0
query = string.gsub(query, "&", "&")
query = string.gsub(query, "<", "<")
query = string.gsub(query, ">", ">")
local function ginsert(qstr)
local first, last = string.find(qstr, "=")
if first then
local pos = string.sub(qstr, 0, first-1)
parsed[pos] = string.sub(qstr, first+1)
end
end
while true do
local first, last = string.find(query, "&", pos)
if first then
ginsert(string.sub(query, pos, first-1));
pos = last+1
else
ginsert(string.sub(query, pos));
break;
end
end
return parsed
end
Due to some bugs related to how Android handles URLs in Honeycomb and ICS, I decided to pass parameters using JASON, eg. we can't simply do
native.showWebPopup("index.html?action=init")
Hence, we can also send parameters without using a TCP server. Initially, I was using this function together with the next piece of code and a web popup URL listener to handle the communication between lua and javascript. But the approach doesn't work with all devices.
-- Write values to be used by webpopup
function setArgs( args )
local path = system.pathForFile( "args.json",
system.DocumentsDirectory )
local file, errStr = io.open( path, "w+b" )
if file then
local newstr = ""
for k,v in pairs(args) do
if k ~= nil and v ~= nil then
if newstr ~= "" then
newstr = newstr .. ", "
end
local val = ""
if type(v) == "boolean" or
tonumber(v) ~= nil then
val = tostring(v)
else
val = "\"" .. v .. "\""
end
newstr = newstr .. "\"" .. k .. "\": " .. val
end
end
local content = "{ " .. newstr .. " }"
file:write( content )
file:close()
return true
end
end
Now we write a function to initialize the TCP server, create a web popup and intercept client requests. The next piece of code illustrates how to do it.
local function doStuff( args )
-- We set the initial parameters so
-- they can immediately be used in our webpage.
setArgs( args )
if runTCPServer ~= nil then
Runtime:removeEventListener( "enterFrame",
runTCPServer )
end
-- We define a function to intercept
-- and process client requests.
runTCPServer = function()
tcpServer:settimeout( 0 )
local tcpClient , _ = tcpServer:accept()
if tcpClient ~= nil then
local tcpClientMessage , _=tcpClient:receive('*l')
if tcpClient ~= nil then
tcpClient:send(20)
tcpClient:close()
end
if ( tcpClientMessage ~= nil ) then
local myMessage = tcpClientMessage
local event = {}
local xArgPos = string.find( myMessage, "?" )
if xArgPos then
local m = string.sub( myMessage, xArgPos+1 )
local newargs = getArgs(m)
if newargs.shouldLoad == nil or
newargs.shouldLoad == "false" then
native.cancelWebPopup()
else
-- do some stuff ...
newargs.arg = tostring(os.date( "*t" ))
setArgs(newargs)
-- or you can use send
-- tcpClient:send( "some stuff" .. "\n")
end
end
end
end
end
if tcpServer == nil then
tcpServer, _ = createTCPServer( "8087" )
end
Runtime:addEventListener( "enterFrame" , runTCPServer )
-- We set the base url to point to our
-- documents directory. To make things
-- easier you can pack all your resources
-- into a tar file and then uncompress it
-- there. A routine to do such this is available.
local options = {
--hasBackground = false,
baseUrl = system.DocumentsDirectory,
--urlRequest = function( event ) return true end
}
-- We open the HTML page.
native.showWebPopup("index.html", options )
end
3 - Creating a Javascript client
Finally, we have to write a javascript client to communicate with our lua TCP server. Hence, we need two javascript methods: getArgs, to read JSON values from a file called "args.json", and getVariable( name, defaultValue ) to later on access these values. var urlVars = {}
function getArgs(){
try {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', "args.json", false);
xobj.send(null);
return eval("(" + xobj.responseText + ")");
}
catch(err){}
return null;
}
function getVariable( name, defaultValue ){
try {
if (urlVars[name] == null) return defaultValue;
return urlVars[name];
}
catch(err){}
return defaultValue;
}
Then we simply use an AJAX call to request or send data. After sending the data through the TCP server, we call the function update to make values available.
function update( ){
urlVars = getArgs();
var arg = getVariable( 'arg', 1 );
var tag = document.getElementById("method")
tag.style.display = 'block';
tag.innerHTML = arg;
return true
}
function send( params ){
var xobj = new XMLHttpRequest();
var server_ip = "http://127.0.0.1:8087"
xobj.open('GET', server_ip + "?" + params, true);
xobj.onreadystatechange = update( );
xobj.send(null);
return true
}
try{
window.onload = function () {
update();
};
}
catch(err){}
One can obtain the full source code here
References
[1] Corona SDK. anscamobile.com. 2012.[2] Jerome Guinot. The LUA SocketLib and the Coroutines. ozone3d.net, 2006.

