EMILUA(3em)

πŸ“¦ version 0.12.1, in nixos 26.05, nixpkgs unstable

🌸 NAME

Emilua β€” the Lua execution engine πŸ’œ (this is a page from the Emilua reference manual, and bestie, we're deep in the ip.tcp.socket section, so buckle up πŸŽ€)


✨ SYNOPSIS

Okay so here's the vibe β€” two sockets, one fiber reading, main thread writing, and zero drama:

-- `socket_pair()` implementation is
-- left as an exercise for the reader
local a, b = socket_pair()

spawn(function()
    local buf = byte_span.new(1024)
    local nread = b:read_some(buf)
    print(buf:first(nread))
end):detach()

local nwritten = stream.write_all(a, 'Hello World')
print(nwritten)

Like, spawn kicks off a fiber, it reads into a byte_span, and meanwhile we write 'Hello World' through stream.write_all β€” and both of them print their byte counts. Iconic. πŸ”₯


πŸ’… FUNCTIONS

new() β†’ ip.tcp.socket

Constructor. That's it, that's the whole thing πŸ’… it makes you a socket.

open(self, address_family: v4"|"v6"|ip.address)"

Opens the socket up, slay.

address_family can be either "v4" or "v6". And if you hand it an ip.address object instead, it'll infer the right value for you like the main character it is ✨

bind(self, addr: ip.address|string, port: integer)

Binds the socket to the given local endpoint. Like claiming your seat, no cap πŸͺ‘

close(self)

Closes the socket. Bye bestie πŸ‘‹

It just forwards the call to the close function with the same name in Boost.Asio:

Any asynchronous send, receive or connect operations will be cancelled immediately, and will complete with the boost::asio::error::operation_aborted error.

cancel(self)

Cancels all asynchronous operations associated with the acceptor.

Also a forward to the cancel function with the same name in Boost.Asio:

This function causes all outstanding asynchronous connect, send and receive operations to finish immediately, and the handlers for cancelled operations will be passed the boost::asio::error::operation_aborted error.

assign(self, address_family: v4"|"v6"|ip.address, fd: file_descriptor)"

Assigns an existing native socket to self. Reusing, we love to see it ♻️

address_family can be either "v4" or "v6". And if you pass an ip.address object, the appropriate value gets inferred πŸ’–

release(self) β†’ file_descriptor

Releases ownership of the native descriptor implementation β€” like handing over the aux cord, the descriptor is yours now 🎧

Forwards to the release function with the same name in Boost.Asio:

This function causes all outstanding asynchronous connect, send and receive operations to finish immediately, and the handlers for cancelled operations will be passed the boost::asio::error::operation_aborted error. Ownership of the native socket is then transferred to the caller.

io_control(self, command: string[, ...])

Performs an IO control command on the socket. Fancy I/O business πŸ”§

Currently available commands are:

  • "bytes_readable" β€” expects no arguments. Gets the amount of data that can be read without blocking. It implements the FIONREAD IO control command. So basically "how much can I slurp without waiting" πŸ“

shutdown(self, what: receive"|"send"|"both")"

Disables sends or receives on the socket. Half-closing, we're being efficient πŸ’…

what can be one of these:

  • "receive" β€” shuts down the receive side of the socket.
  • "send" β€” shuts down the send side of the socket.
  • "both" β€” shuts down both send and receive on the socket. All of it. Periodt.

connect(self, addr: ip.address, port: integer)

Starts a connect operation and blocks the current fiber until it finishes or errs. It's giving "wait for me" energy 😭

disconnect(self)

Dissolves the socket's association by resetting the socket's peer address (i.e. connect(3) will be called with an AF_UNSPEC address). Basically "we're strangers now" πŸ’€

read_some(self, buffer: byte_span) β†’ integer

Reads data from the stream socket and blocks the current fiber until it completes or errs.

Returns the number of bytes read. ⭐

write_some(self, buffer: byte_span) β†’ integer

Writes data to the stream socket and blocks the current fiber until it completes or errs.

Returns the number of bytes written. ⭐

receive(self, buffer: byte_span, flags: string[]) β†’ integer

Reads data from the stream socket and blocks the current fiber until it completes or errs.

Returns the number of bytes read. (Yeah, same description as read_some, I'm not gonna pretend otherwise πŸ™ˆ)

send(self, buffer: byte_span, flags: string[]) β†’ integer

Writes data to the stream socket and blocks the current fiber until it completes or errs.

Returns the number of bytes written. ✨

send_file(self, file: file.random_access, offset: integer, size_in_bytes: integer[, head: byte_span[, tail: byte_span[, n_number_of_bytes_per_send: integer]]]) β†’ integer

A wrapper for the TransmitFile() function.

Note Only available on Windows. πŸͺŸ
Important Lua conventions on index starting at 1 are ignored. Indexes here are OS-mandated and start at 0. Yes, genuinely. Do not come for me, I'm just the messenger πŸ’€

Reference: https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile

wait(self, wait_type: read"|"write"|"error")"

Waits for the socket to become ready to read, ready to write, or to have pending error conditions.

In short: the reactor model is exposed on top of the proactor model. Wild, I know.

Important You shouldn't be using reactor-style operations on Emilua. BUT β€” there's this one obsolete and buggy TCP feature that presumes reactor-style operations: SO_OOBINLINE (out_of_band_inline) + sockatmark() (at_mark). So if you're implementing an ancient obscure protocol that for some reason can avoid the TCP OOB bugs, then you'll need to use this function. Only then. I said what I said πŸ’…

wait_type can be one of the following:

  • "read" β€” waits for a socket to become ready to read.
  • "write" β€” waits for a socket to become ready to write.
  • "error" β€” waits for a socket to have error conditions pending.

set_option(self, opt: string, val)

Sets an option on the socket. Little knobs, big power πŸ”₯

Currently available options are:

Important Do bear in mind that the BSD socket API for SO_OOBINLINE is incompatible with proactor-style operations. Like, actually incompatible. Not a vibe. πŸ’€ Reference: http://www.serverframework.com/asynchronousevents/2011/10/out-of-band-data-and-overlapped-io.html

get_option(self, opt: string) β†’ value

Gets an option from the socket. Same knobs, now you're reading them πŸ‘‘

Currently available options are:


πŸŽ€ FUNCTION FLAGS

These are the flags you can hand to receive / send (the flags: string[] ones, remember?), and each one is literally the Boost.Asio flag with the same name πŸ’–

do_not_route

The do_not_route flag with the same name in Boost.Asio:

Specify that the data should not be subject to routing.

(https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/reference/socket_base/message_do_not_route.html)

end_of_record

The end_of_record flag with the same name in Boost.Asio:

Specifies that the data marks the end of a record.

(https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/reference/socket_base/message_end_of_record.html)

out_of_band

The out_of_band flag with the same name in Boost.Asio:

Process out-of-band data.

(https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/reference/socket_base/message_out_of_band.html)

peek

The peek flag with the same name in Boost.Asio:

Peek at incoming data without removing it from the input queue.

(https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/reference/socket_base/message_peek.html)

Peeking is so main character energy β€” look but don't touch πŸ¦‹

✨ PROPERTIES ✨

okay bestie, this is the good part β€” all the little attributes you can peek at on your socket πŸ’… each one's got a type and a whole personality, let's get into it.

πŸŽ€ is_open: boolean

literally just tells you whether the socket is open or not. that's it. that's the whole vibe check. open = true, closed = false, no cap πŸ¦‹

🌸 local_address: ip.address

the local address endpoint of the socket β€” like, where you are on the network, bestie. it's giving "this is my house" 🏠

πŸ’– local_port: integer

the local port endpoint of the socket. the number your side is chillin' on πŸŽ€

πŸ¦‹ remote_address: ip.address

the remote address endpoint of the socket β€” where the other side lives. the pen pal address, if you will ✨

πŸ’œ remote_port: integer

the remote port endpoint of the socket. the port number on the other end, slay 🌈

πŸ”₯ at_mark: boolean

whether the socket is at the out-of-band data mark. ooooh fancy πŸ‘‘

⚠️ Important ⚠️ bestie listen to me fr fr β€” you must set the out_of_band_inline socket option and use reactor-style operations (wait()) to use this feature. no shortcuts, no vibes-only approach, it literally will not work otherwise πŸ˜­πŸ’€

and that's the whole page, bestie!! you made it to the end, proud of you 🫢 this icon of a manpage is from Emilua 0.12.1-nixpkgs1, dated 1980-01-01, and it lives in section EMILUA(3em) β€” go forth and socket responsibly, mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from