Tutorial:Networking with UDP-TheServer
The complete source for the UDP server
local socket = require "socket" -- begin local udp = socket.udp() -- normally socket reads block until they have data, or a -- certain amount of time passes. -- that doesn't suit us, so we tell it not to do that by setting the -- 'timeout' to zero udp:settimeout(0) -- unlike the client, the server has to be specific about where its -- 'bound', or the poor clients will never find it. -- thus while we can happily let the client auto-bind to whatever it likes, -- we have to tell the server to bind to something known. -- -- the first part is which "interface" we should bind to...a bit beyond this tutorial, but '*' basically means "all of them" -- port is simpler, the system maintains a list of up to 65535 (!) "ports" -- ...really just numbers. point is that if you send to a particular port, -- then only things "listening" to that port will be able to receive it, -- and likewise you can only read data sent to ports you are listening too. -- generally speaking, if an address is which machine you want to talk to, then a port is what program on that machine you want to talk to. -- -- [NOTE: on some operating systems, ports between 0 and 1024 are "reserved for -- privileged processes". its a security precaution for those system. -- generally speaking, just not using ports in that range avoids a lot of problems] udp:setsockname('*', 12345) local world = {} -- the empty world-state -- We declare a whole bunch of local variables that we'll be using the in -- main server loop below. you probably recognise some of them from the --client example, but you are also probably wondering what's with the fruity -- names, 'msg_or_ip'? 'port_or_nil'? -- -- well, we're using a slightly different function this time, you'll see when we get there. local data, msg_or_ip, port_or_nil local entity, cmd, parms -- indefinite loops are probably not something you used to if you only -- know love, but they are quite common. and in fact love has one at its -- heart, you just don't see it. -- regardless, we'll be needing one for our server. and this little -- variable lets us *stop* it :3 local running = true -- the beginning of the loop proper... print "Beginning server loop." while running do -- this line looks familiar, I'm sure, but we're using 'receivefrom' -- this time. its similar to receive, but returns the data, sender's -- ip address, and the sender's port. (which you'll hopefully recognise -- as the two things we need to send messages to someone) -- we didn't have to do this in the client example because we just bound -- the socket to the server. ...but that also ignores messages from -- sources other than what we've bound to, which obviously won't do at -- all as a server. -- -- [NOTE: strictly, we could have just used receivefrom (and its -- counterpart, sendto) in the client. there's nothing special about the -- functions to prevent it, indeed. send/receive are just convenience -- functions, sendto/receive from are the real workers.] data, msg_or_ip, port_or_nil = udp:receivefrom() if data then -- more of these funky match paterns! entity, cmd, parms = data:match("^(%S*) (%S*) (.*)") if cmd == 'move' then local x, y = parms:match("^(%-?[%d.e]*) (%-?[%d.e]*)$") assert(x and y) -- validation is better, but asserts will serve. -- don't forget, even if you matched a "number", the result is still a string! -- thankfully conversion is easy in lua. x, y = tonumber(x), tonumber(y) -- and finally we stash it away local ent = world[entity] or {x=0, y=0} world[entity] = {x=ent.x+x, y=ent.y+y} elseif cmd == 'at' then local x, y = parms:match("^(%-?[%d.e]*) (%-?[%d.e]*)$") assert(x and y) -- validation is better, but asserts will serve. x, y = tonumber(x), tonumber(y) world[entity] = {x=x, y=y} elseif cmd == 'update' then for k, v in pairs(world) do udp:sendto(string.format("%s %s %d %d", k, 'at', v.x, v.y), msg_or_ip, port_or_nil) end elseif cmd == 'quit' then running = false; else print("unrecognised command:", cmd) end elseif msg_or_ip ~= 'timeout' then error("Unknown network error: "..tostring(msg)) end socket.sleep(0.01) end print "Thank you." -- and that the end of the udp server example.