I haven't written any IP-related code since I retired four years ago, so I have forgotten a lot of the fine points. The address and port stuff is at least partially due to byte order: on the wire, IP sends addresses and ports most significant byte first. On a PC, integers are stored least significant byte first. The sockets API (which ljsockets more or less duplicates) dates back to the dawn of IP, when computers were slow, so the API tries to minimize byte order conversion. So in the C API there are/were platform-specific functions "ntohl()" (network to host long), "htonl" (host to network long) etc. that you had to use to turn integers into IP addresses and ports, and vice versa.
I would have hoped that a Lua sockets library would have hidden all that, but looking at ljsocket.lua, you can see that it just wraps the C API, and I don't know the details of ffi to see exactly what is happening.
If I call get_port() on the address returned along with data by receive_from, the value I get is the byte-flipped version of the UDP source port from which the message came (not the UDP destination port that I am bound to and listening on). So if the source port is 4097 (0x1001), get_port() returns 272 (0x0110). get_ip() returns a string that is mangled even more than byte order - I haven't figured it out yet, but the first two bytes seem to be the port rather than part of the address.
BUT: I can USE the address as returned by receive_from as the address for send_to(), and my datagram will be sent there.
I also note that if I do such a send, and nobody is listening on that address and port, my socket is closed and subsequent receive_from calls fail. There is probably a flag or option to prevent that (something like the "reuseaddr" in the example), but I don't recall it and Google isn't helping. Or you could create a second socket just to send the datagram, and then close it.