To use LuaSocket in Gideros you need to include socket.lua file to your project. You can find socket.lua file in your Gideros installation folder, inside All Plugins/LuaSocket/source.

The basic idea how this works, as that you provide an IP address of your current device and a port number to listen to. Then other devices can send information to specified IP address and specified port and you'll receive it.

Getting device's IP address

But how would you get you device's IP address?

Devices may have multiple interfaces through which they connect to the Internet, for example, Wi-Fi Card, LAN cable slot, 3g/4g connection, modem, etc. Unfortunately I haven't found a way to get list of available interfaces, but there is an interesting hack to get the IP address of available connection. Basically you create an UDP socket and set it to connect to outside peer (Google website in this case). Then we can get an IP address, through which socket was going to connect to this remote peer. But connection was never established, so you don't even need an outside Internet connection to do this, all can happen inside Local Area Network.

So here is a function to get an IP address:

local function getIP()
	local s = socket.udp()
	s:setpeername("74.125.115.104",80)
	local ip, _ = s:getsockname()
	return ip
end

But what about ports? For port you can use any number between 1 and 65535. But sometimes choosing port that is already assigned to other service, may create conflicts, so it is better to avoid Well-known ports and choose your port as a number from 1024 to 65535

Now we can get and IP address to set for our sockets for listening to specific port. What to do next?

So there are two protocols available (actually there are more, but this are the two basic ones):

  • TCP, which is a connection oriented protocol, it ensures that package we sent will be delivered to destination.
  • UDP, which is a connectionless protocol, it sends out information, without knowing if it was ever delivered.

TCP is a reliable protocol, while UDP is much faster. They even say that in Local Networks UDP is as reliable as TCP. What to use for you application depends on your needs. Many games use both protocols by sending important information through TCP and other data through UDP for faster connection. When working with both protocols, keep in mind, that when binding server to listen to one of the ports, you cannot assign listening to the same port (same IP addresses can be used) using other protocol or other connection. Basically use different ports for each listening instance

UDP protocol

So let's start with UDP, because it's a bit easier to grasp and implement

--first let's create a UDP instance
local udp = socket.udp()
--then if we want to receive an data, we need to bind it to 
--our IP address and some port
--if we want only to send data, this is not needed
udp:setsockname(getIP(), 1234)

--another important thing to do is to set time out for socket instance
--for example if we want to receive something, we call receive function 
--(in tcp also accepting new clients)
--as everything is executed in the same thread, 
--the call of receive function would stop execution
--until we actually receive something.
--To make it try to receive something and continue 
--if there is nothing to be received, we need to set timer
--for how long should be receive.
--And as we do not want to block our application UI from responding, 
--this time out period will be 0 seconds
--meaning we won't waste any time waiting, 
--simply check if there is something, get it, if not continue
udp:settimeout(0)


--and every time we will want to send some information, 
--we simply provide IP address and port where to send it
udp:sendto("This is text, which will be sent", "192.168.50.101", 1234)

So now we can send information, lets try to receive something

Basically we need to check if there is anything to be received from time to time. Usually it is done through eternal loop from another thread. But since we have only one thread, we can use timers for that purpose. As I understand in Gideros, timers emulate separate thread behavior, so again it does not block UI of our app

--running timer 10 times each second should be sufficient
local timer = Timer.new(100)
 
--call on each timer tick
timer:addEventListener(Event.TIMER, function()
	--to ensure responsiveness of our application
	--we should better loop through all available information at one timer call
	repeat
		--try to get any data
		local data, ip, port = udp:receivefrom()
		--if there is data
		if data then 
			--do something with it
		end
		--repeat until there is no data to receive
	until not data
end)
 
--start the timer
timer:start()

TCP protocol

Unlike the UDP, TCP is a little more complex. If in UDP we could simple send information and listen if someone sent something to us, and there was no strict difference between server and client, in TCP there is one.

First let's start with the client. Unlike UDP, we don't need to bind and listen to specific IP address and port to send something. Once server accepts our connection, there is a link established, through which we can send information. We only need to connect to server.

--first create tcp instance
local tcp = socket.tcp()

--then connect it to server's IP and specific port
tcp:connect("192.168.50.101", 1235)

--and same as UDP set time out
--so it won't block UI
tcp:settimeout(0)

And similar to UDP we should listen for new messages using Timers

--running timer 10 times each second should be sufficient
local timer = Timer.new(100)
 
--call on each timer tick
timer:addEventListener(Event.TIMER, function()
	--to ensure responsiveness of our application
	--we should better loop through all available information at one timer call
	repeat
		--try to get any data
		local data, err = tcp:receive()
		--if there is data
		if data then 
			--do something with it
		end
		--repeat until there is no data to receive
	until not data
end)
 
--start the timer
timer:start()

Now let's create a server that would handle multiple clients. First let's start with creating server instance

--as a server we need to bind socket to specific IP address and port
--to which we would listen
local tcp = socket.bind("192.168.50.101", 1235)

--set timeout so it won't block UI
tcp:settimeout(0)

Now we'll need to both check for new clients and check if there is anything new sent to us.

--here we will store our clients
local clients = {}

--running timer 10 times each second should be sufficient
local timer = Timer.new(100)
 
--call on each timer tick
timer:addEventListener(Event.TIMER, function()
	--first we loop through all new clients connections
	repeat
		--try to accept connection from client
		local client = tcp:accept()
		
		--if there is a new client
		if client then
			--set it's time out, so it won't block our UI
			client:settimeout(0)
			--and add it to the client list
			clients[#clients+1] = client
		end
		--repeat until there is no new connection
	until not client
	
	--after that we can check which clients want to send anything to us
	--clients is a table with all available clients
	--we pass it to the socket.select function
	--first parameter to check for all clients, that have sent something to us
	--second parameter for all clients, that we can send to
	--third parameter is a timeout, which we provide as 0 as usually
	--and this function returns list with clients that sent something to us
	--list with clients that we can send to
	--and error message if any
	local ready, writeReady, err = socket.select(clients, clients, 0)
	
	--check if there was no error
	if err == nil then
		--we can loop through clients
		--that sent us something
		for i = 1, #ready do
			local client = ready[i]
			--again let's try to read all data that someone sent to us
			repeat
				local data, err = client:receive()
				--if there is data
				if data then 
					--do something with it
				end
				--repeat until there is no data to receive
			until not data
		end
	end
end)
 
--start the timer
timer:start()

Actually we can ignore the list with clients to which we can send information, because TCP is a reliable protocol, they will still receive that message, even if they are busy right now. So theoretically we can send information to devices whenever we want.

--loop through all the clients
for i = 1, #clients do
	--and send them some text
	local client = clients[i]
	--tcp only
	--don't forget to apply new line symbol to the end of messages
	--so it could be received without specific parameters
	client:send("ping\n")
end

Broadcasting and multicasting

So we can create servers and clients and interact with each other. But to do that, we need to know IP address of server. Is there a way to discover devices automatically? Yes there is.

Server can broadcast messages using UDP broadcast, meaning that all devices on the same network should receive that message. But as usually it's not so simple. Broadcast messages could be ignored due to different configuration settings, which you might not control. So there is another option, using multicast. Basically one specific IP address is chosen, and all other devices add their IP address to specified multicast address group. And when you send your message to this multicast IP address, all messages are forwardet to devices that were added to this multicast group.

Again it does not work for all devices (for example, I can't make my Ipod a part of multicst group), so combination of broadcasting and multicasting should be used to cover different cases and discover devices.

So let's start with server this time. We simply create UDP instance in which we broadcast and multicast messages

--create udp instance for broadcasting.multicasting
local send = socket.udp()
--set timeout so it won't block UI
send:settimeout(0)

--running timer 10 times each second should be sufficient
local timer = Timer.new(100)
 
--call on each timer tick
timer:addEventListener(Event.TIMER, function()
	--message we will send
	local msg = "All devices unite"
	
	--first we send to multicast group
	--multicast IP range from 224.0.0.0 to 239.255.255.255
	--we simple select on and use the same in clients' code
	send:sendto(msg, "239.192.1.1", 11111)
	
	--then we enable broadcast option
	send:setoption('broadcast', true)
	--and broadcast the message
	--global broadcast address is 255.255.255.255
	send:sendto(msg, "255.255.255.255", 11111)
	--and we disable broadcast option
	send:setoption('broadcast', false)
end)
 
--start the timer
timer:start()

And what needs to be done on client's side, is simply create udp instance with multicast IP address if possible (or it's own if not) and listen

--create udp instance fir listening broadcasts/multicasts
local listen = socket.udp()

--try to set socket for multicast
--multicast IP range from 224.0.0.0 to 239.255.255.255
listen:setsockname("239.192.1.1", 11111)

--check if device supports multicast
local name = listen:getsockname()
--if supports
if(name)then
	--add interface to multicast
	self.listen:setoption("ip-add-membership" , 
		{ multiaddr = "239.192.1.1", interface = getIP()})
else
	--else set it's own IP address 
	--and will hope that broadcast messages work

	listen = socket.udp()
	listen:setsockname(self.ip, self.port3)
end

--set timeout so it won't block UI
listen:settimeout(0)

Then we would simply listen to specific message, which would indicate that it's an attempt to discover us and then we simply get server IP address. The rest you know, we use server's IP address to send messages using TCP or UDP

--running timer 10 times each second should be sufficient
local timer = Timer.new(100)
 
--call on each timer tick
timer:addEventListener(Event.TIMER, function()
	--to ensure responsiveness of our application
	--we should better loop through all available information at one timer call
	repeat
		--try to get any data and sender's IP address
		local data, ip, port = udp:receivefrom()
		--if there is data
		if data and data == "All devices unite" then 
			--we have a server trying to discover us
			--it's IP address is stored in ip variable
		end
		--repeat until there is no data to receive
	until not data
end)
 
--start the timer
timer:start()

Here is a more detailed LuaSocket documentation (unofficial, but describes a lot of undocumented features): https://github.com/sam-github/luasocket/tree/unstable/doc

That’s it, you can create your own network of devices, using any of the devices as client or server