Module socket
The socket module allows exchanging data via BSD sockets with a local
or remote host in connection-oriented (TCP) or datagram-oriented (UDP)
mode. Semantics of the calls in the socket API closely follow
semantics of the corresponding POSIX calls.
The functions for setting up and connecting are socket, sysconnect,
tcp_connect. The functions for sending data are send, sendto,
write, syswrite. The functions for receiving data are recv,
recvfrom, read. The functions for waiting before sending/receiving
data are wait, readable, writable. The functions for setting flags
are nonblock, setsockopt. The functions for stopping and
disconnecting are shutdown, close. The functions for error checking
are errno, error.
Below is a list of all socket functions.
Name | Use |
|---|---|
Create a socket | |
Connect a socket to a remote host | |
Get information about a remote site | |
Make Tarantool act as a TCP server | |
Bind a socket to the given host/port | |
Connect a socket to a remote host | |
Send data over a connected socket | |
Write data to the socket buffer if non-blocking | |
Read from a connected socket | |
Read data from the socket buffer if non-blocking | |
Bind a socket to the given host/port | |
Start listening for incoming connections | |
Accept a client connection + create a connected socket | |
Send a message on a UDP socket to a specified host | |
Receive a message on a UDP socket | |
Shut down a reading end, a writing end, or both | |
Close a socket | |
Get information about the last error on a socket | |
Set socket flags | |
Get socket flags | |
Set/clear the SO_LINGER flag | |
Set/get the flag value | |
Wait until something is readable | |
Wait until something is writable | |
Wait until something is either readable or writable | |
Get information about the connection's near side | |
Get information about the connection's far side | |
Wait for read/write activity | |
Several methods for emulating the LuaSocket API |
Typically a socket session will begin with the setup functions, will set one or more flags, will have a loop with sending and receiving functions, will end with the teardown functions – as an example at the end of this section will show. Throughout, there may be error-checking and waiting functions for synchronization. To prevent a fiber containing socket functions from "blocking" other fibers, the implicit yield rules will cause a yield so that other processes may take over, as is the norm for cooperative multitasking.
For all examples in this section the socket name will be sock and the
function invocations will look like sock:function_name(...).
__call(domain, type, protocol)
Create a new TCP or UDP socket. The argument values are the same as in the Linux socket(2) man page.
Returns
an unconnected socket, or nil.
Return type
userdata
Example:
socket('AF_INET', 'SOCK_STREAM', 'tcp')
Connect a socket to a remote host.
Parameters:
-
host(string) — URL or IP address -
port(number) — port number -
timeout(number) — number of seconds to wait
Returns
(if error) {nil, error-message-string}. (if no error) a new socket object.
Return type
socket object, which may be viewed as a table
Example:
sock, e = socket.tcp_connect('127.0.0.1', 3301)if sock == nil then print(e) end
The socket.getaddrinfo() function is useful for finding information
about a remote site so that the correct arguments for
sock:sysconnect() can be passed. This function may use the
worker_pool_threads configuration
parameter.
Parameters:
-
host(string) — URL or IP address -
port(number/string) — port number as a numeric or string -
timeout(number) — maximum number of seconds to wait -
options(table) — -type– preferred socket typefamily– desired address family for the returned addressesprotocolflags– additional options (see details here)
Returns
(if error) {nil, error-message-string}. (if no error) A table containing these fields: "host", "family", "type", "protocol", "port".
Return type
table
Example:
tarantool> socket.getaddrinfo('tarantool.org', 'http')---- - host: 188.93.56.70family: AF_INETtype: SOCK_STREAMprotocol: tcpport: 80- host: 188.93.56.70family: AF_INETtype: SOCK_DGRAMprotocol: udpport: 80...-- To find the available values for the options use the following:tarantool> socket.internal.AI_FLAGS -- or SO_TYPE, or DOMAIN---- AI_ALL: 256AI_PASSIVE: 1AI_NUMERICSERV: 4096AI_NUMERICHOST: 4AI_V4MAPPED: 2048AI_ADDRCONFIG: 1024AI_CANONNAME: 2...
The socket.tcp_server() function makes Tarantool act as a server that
can accept connections. Usually the same objective is accomplished with
box.cfg{listen=...}.
Parameters:
-
host(string) — host name or IP -
port(number) — host port, may be 0 -
handler-function-or-table(function/table) — what to execute when a connection occurs -
timeout(number) — host resolving timeout in seconds
Returns
(if error) {nil, error-message-string}. (if no error) a new socket object.
Return type
socket object, which may be viewed as a table
The handler-function-or-table parameter may be simply a function name /
function declaration: handler_function. Or it may be a table:
{handler =
{handler_function} [, prepare = {prepare_function}] [, name = {name}] }. handler_function is mandatory; it may have a
parameter = the socket; it is executed once after accept() happens (once
per connection); it is for continuous operation after the connection is
made. prepare_function is optional; it may have parameters = the
socket object and a table with client information; it should return
either a backlog value or nothing; it is executed only once before
bind() on the listening socket (not once per connection). Examples:
socket.tcp_server('localhost', 3302, function (s) loop_loop() end)socket.tcp_server('localhost', 3302, {handler=hfunc, name='name'})socket.tcp_server('localhost', 3302, {handler=hfunc, prepare=pfunc})
For fuller examples see Use tcp_server to accept file contents sent with socat and Use tcp_server with handler and prepare.
Bind a socket to the given host/port. This is equivalent to
socket_object:bind(), but is done on the result of
require('socket'), rather than on the socket object.
Parameters:
-
host(string) — URL or IP address -
port(number) — port number
Returns
(if error) {nil, error-message-string}. (if no error) A table which may have information about the bind result.
Return type
table
socket_object
Connect an existing socket to a remote host. The argument values are the same as in tcp_connect(). The host must be an IP address.
Parameters:
: -
Either:: - host - a string representation of an IPv4 address or an IPv6address;- port - a number.-Or:: - host - a string containing "unix/";- port - a string containing a path to a unix socket.-Or:: - host - a number, 0 (zero), meaning "all local interfaces";- port - a number. If a port number is 0 (zero), the socketwill be bound to a random local port.
<!– –>
Returns
the socket object value may change if sysconnect() succeeds.
Return type
boolean
Example:
socket = require('socket')sock = socket('AF_INET', 'SOCK_STREAM', 'tcp')sock:sysconnect(0, 3301)
Send data over a connected socket.
data(string) — what is to be sent
Returns
the number of bytes sent.
Return type
number
Possible errors: nil on error.
Write as much data as possible to the socket buffer if non-blocking. Rarely used. For details see this description.
Read size bytes from a connected socket. An internal read-ahead buffer
is used to reduce the cost of this call.
size(integer) — maximum number of bytes to receive. See Recommended size.
Returns
a string of the requested length on success.
Return type
string
Possible errors: On error, returns an empty string, followed by status, errno, errstr. In case the writing side has closed its end, returns the remainder read from the socket (possibly an empty string), followed by "eof" status.
[, timeout])
Read from a connected socket until some condition is true, and return
the bytes that were read. Reading goes on until limit bytes have been
read, or a delimiter has been read, or a timeout has expired. Unlike
socket_object:recv (which uses an internal read-ahead buffer),
socket_object:read depends on the socket's buffer.
-
limit(integer) — maximum number of bytes to read, for example 50 means "stop after 50 bytes" -
delimiter(string) — separator for example?means "stop after a question mark"; this parameter can accept a table of separators, for example,delimiter = {"\n", "\r"} -
timeout(number) — maximum number of seconds to wait, for example 50 means "stop after 50 seconds". -
options(table) —chunk={limit}and/ordelimiter={delimiter}, for example{chunk=5,delimiter='x'}.
Returns
an empty string if there is nothing more to read, or a nil value if
error, or a string up to limit bytes long, which may include the
bytes that matched the delimiter expression.
Return type
string
Return data from the socket buffer if non-blocking. In case the socket
is blocking, sysread() can block the calling process. Rarely used. For
details, see also this
description.
size(integer) — maximum number of bytes to read, for example 50 means "stop after 50 bytes"
Returns
an empty string if there is nothing more to read, or a nil value if
error, or a string up to size bytes long.
Return type
string
Bind a socket to the given host/port. A UDP socket after binding can be used to receive data (see socket_object.recvfrom). A TCP socket can be used to accept new connections, after it has been put in listen mode.
-
host(string) — URL or IP address -
port(number) — port number
Returns
true for success, false for error. If return is false, use socket_object:errno() or socket_object:error() to see details.
Return type
boolean
Start listening for incoming connections.
backlog— on Linux the listenbacklogbacklog may be from/proc/sys/net/core/somaxconn, on BSD the backlog may beSOMAXCONN.
Returns
true for success, false for error.
Return type
boolean.
Accept a new client connection and create a new connected socket. It is good practice to set the socket's blocking mode explicitly after accepting.
Returns
new socket if success.
Return type
userdata
Possible errors: nil.
Send a message on a UDP socket to a specified host.
-
host(string) — URL or IP address -
port(number) — port number -
data(string) — what is to be sent
Returns
the number of bytes sent.
Return type
number
Possible errors: on error, returns nil and may return status, errno, errstr.
Receive a message on a UDP socket.
size(integer) — maximum number of bytes to receive. See Recommended size.
Returns
message, a table containing "host", "family" and "port" fields.
Return type
string, table
Possible errors: on error, returns status, errno, errstr.
Example:
After message_content, message_sender = recvfrom(1) the value of
message_content might be a string containing 'X' and the value of
message_sender might be a table containing
message_sender.host = '18.44.0.1'message_sender.family = 'AF_INET'message_sender.port = 43065
Shutdown a reading end, a writing end, or both ends of a socket.
how— socket.SHUT_RD, socket.SHUT_WR, or socket.SHUT_RDWR.
Returns
true or false.
Return type
boolean
Close (destroy) a socket. A closed socket should not be used any more. A socket is closed automatically when the Lua garbage collector removes its user data.
Returns
true on success, false on error. For example, if sock is already closed, sock:close() returns false.
Return type
boolean
Retrieve information about the last error that occurred on a socket, if any. Errors do not cause throwing of exceptions so these functions are usually necessary.
Returns
result for sock:errno(), result for sock:error(). If there is no
error, then sock:errno() will return 0 and sock:error().
Return type
number, string
Set socket flags. The argument values are the same as in the Linux getsockopt(2) man page. The ones that Tarantool accepts are:
- SO_ACCEPTCONN
- SO_BINDTODEVICE
- SO_BROADCAST
- SO_DEBUG
- SO_DOMAIN
- SO_ERROR
- SO_DONTROUTE
- SO_KEEPALIVE
- SO_MARK
- SO_OOBINLINE
- SO_PASSCRED
- SO_PEERCRED
- SO_PRIORITY
- SO_PROTOCOL
- SO_RCVBUF
- SO_RCVBUFFORCE
- SO_RCVLOWAT
- SO_SNDLOWAT
- SO_RCVTIMEO
- SO_SNDTIMEO
- SO_REUSEADDR
- SO_SNDBUF
- SO_SNDBUFFORCE
- SO_TIMESTAMP
- SO_TYPE
Setting SO_LINGER is done with sock:linger(active).
Get socket flags. For a list of possible flags see sock:setsockopt().
Set or clear the SO_LINGER flag. For a description of the flag, see the Linux man page.
active(boolean) — none
Returns
new active and timeout values.
sock:nonblock()returns the current flag value.sock:nonblock(false)sets the flag to false and returns false.sock:nonblock(true)sets the flag to true and returns true.
This function may be useful before invoking a function which might otherwise block indefinitely.
Wait until something is readable, or until a timeout value expires.
Returns
true if the socket is now readable, false if timeout expired;
Wait until something is writable, or until a timeout value expires.
Returns
true if the socket is now writable, false if timeout expired;
Wait until something is either readable or writable, or until a timeout value expires.
Returns
'R' if the socket is now readable, 'W' if the socket is now writable, 'RW' if the socket is now both readable and writable, '' (empty string) if timeout expired;
The sock:name() function is used to get information about the near
side of the connection. If a socket was bound to xyz.com:45, then
sock:name will return information about [host:xyz.com, port:45]. The
equivalent POSIX function is getsockname().
Returns
A table containing these fields: "host", "family", "type", "protocol", "port".
Return type
table
The sock:peer() function is used to get information about the far side
of a connection. If a TCP connection has been made to a distant host
tarantool.org:80, sock:peer() will return information about
[host:tarantool.org, port:80]. The equivalent POSIX function is
getpeername().
Returns
A table containing these fields: "host", "family", "type", "protocol", "port".
Return type
table
The socket.iowait() function is used to wait until read-or-write
activity occurs for a file descriptor.
Parameters:
-
fd— file descriptor -
read-or-write-flags— 'R' or 1 = read, 'W' or 2 = write, 'RW' or 3 = read|write. -
timeout— number of seconds to wait
If the fd parameter is nil, then there will be a sleep until the timeout. If the timeout parameter is nil or unspecified, then timeout is infinite.
Ordinarily the return value is the activity that occurred ('R' or 'W' or 'RW' or 1 or 2 or 3). If the timeout period goes by without any reading or writing, the return is an error = ETIMEDOUT.
Example: socket.iowait(sock:fd(), 'r', 1.11)
The LuaSocket API has functions that are equivalent to the ones
described above, with different names and parameters, for example
connect() rather than tcp_connect(). Tarantool supports these
functions so that third-party packages which depend on them will work.
The LuaSocket project is on github. The API description is in the LuaSocket manual (click the "introduction" and "reference" links at the bottom of the manual's main page).
A Tarantool example is Use of a socket with LuaSocket wrapper functions.
For recv and recvfrom: use the optional size parameter to limit
the number of bytes to receive. A fixed size such as 512 is often
reasonable; a pre-calculated size that depends on context – such as
the message format or the state of the network – is often better. For
recvfrom, be aware that a size greater than the Maximum Transmission
Unit can cause
inefficient transport. For Mac OS X, be aware that the size can be tuned
by changing sysctl net.inet.udp.maxdgram.
If size is not stated: Tarantool will make an extra call to calculate
how many bytes are necessary. This extra call takes time, therefore not
stating size may be inefficient.
If size is stated: on a UDP socket, excess bytes are discarded. On a
TCP socket, excess bytes are not discarded and can be received by the
next call.
In this example a connection is made over the internet between a
Tarantool instance and tarantool.org, then an HTTP "head" message is
sent, and a response is received: "HTTP/1.1 200 OK" or something
else if the site has moved. This is not a useful way to communicate with
this particular site, but shows that the system works.
tarantool> socket = require('socket')---...tarantool> sock = socket.tcp_connect('tarantool.org', 80)---...tarantool> type(sock)---- table...tarantool> sock:error()---- null...tarantool> sock:send("HEAD / HTTP/1.0\r\nHost: tarantool.org\r\n\r\n")---- 40...tarantool> sock:read(17)---- HTTP/1.1 302 Move...tarantool> sock:close()---- true...
This is a variation of the earlier example "Use of a TCP socket over the Internet". It uses LuaSocket wrapper functions, with a too-short timeout so that a "Connection timed out" error is likely. The more common way to specify timeout is with an option of tcp_connect().
tarantool> socket = require('socket')---...tarantool> sock = socket.connect('tarantool.org', 80)---...tarantool> sock:settimeout(0.001)---- 1...tarantool> sock:send("HEAD / HTTP/1.0\r\nHost: tarantool.org\r\n\r\n")---- 40...tarantool> sock:receive(17)---- null- Connection timed out...tarantool> sock:close()---- 1...
Here is an example with datagrams. Set up two connections on 127.0.0.1
(localhost): sock_1 and sock_2. Using sock_2, send a message to
sock_1. Using sock_1, receive a message. Display the received
message. Close both connections.
This is not a useful way for a
computer to communicate with itself, but shows that the system works.
tarantool> socket = require('socket')---...tarantool> sock_1 = socket('AF_INET', 'SOCK_DGRAM', 'udp')---...tarantool> sock_1:bind('127.0.0.1')---- true...tarantool> sock_2 = socket('AF_INET', 'SOCK_DGRAM', 'udp')---...tarantool> sock_2:sendto('127.0.0.1', sock_1:name().port,'X')---- 1...tarantool> message = sock_1:recvfrom(512)---...tarantool> message---- X...tarantool> sock_1:close()---- true...tarantool> sock_2:close()---- true...
Here is an example of the tcp_server function, reading strings from the client and printing them. On the client side, the Linux socat utility will be used to ship a whole file for the tcp_server function to read.
Start two shells. The first shell will be a server instance. The second shell will be the client.
On the first shell, start Tarantool and say:
box.cfg{}socket = require('socket')socket.tcp_server('0.0.0.0', 3302,{handler = function(s)while true dolocal requestrequest = s:read("\n");if request == "" or request == nil thenbreakendprint(request)endend,prepare = function()print('Initialized')end})
The above code means:
- Use
tcp_server()to wait for a connection from any host on port 3302. - When it happens, enter a loop that reads on the socket and prints
what it reads. The "delimiter" for the read function is "\n" so
each
read()will read a string as far as the next line feed, including the line feed.
On the second shell, create a file that contains a few lines. The contents don't matter. Suppose the first line contains A, the second line contains B, the third line contains C. Call this file "tmp.txt".
On the second shell, use the socat utility to ship the tmp.txt file to the server instance's host and port:
$ socat TCP:localhost:3302 ./tmp.txt
Now watch what happens on the first shell. The strings "A", "B", "C" are printed.
Here is an example of the tcp_server function using handler and
prepare.
Start two shells. The first shell will be a server instance. The second shell will be the client.
On the first shell, start Tarantool and say:
box.cfg{}socket = require('socket')sock = socket.tcp_server('0.0.0.0',3302,{prepare =function(sock)print('listening on socket ' .. sock:fd())sock:setsockopt('SOL_SOCKET','SO_REUSEADDR',true)return 5end,handler =function(sock, from)print('accepted connection from: ')print(' host: ' .. from.host)print(' family: ' .. from.family)print(' port: ' .. from.port)end})
The above code means:
- Use
tcp_server()to wait for a connection from any host on port 3302. - Specify that there will be an initial call to
preparewhich displays something about the server, then callssetsockopt(...'SO_REUSEADDR'...)(this is the same option that Tarantool would set if there was noprepare), and then returns 5 (this is a rather low backlog queue size). - Specify that there will be per-connection calls to
handlerwhich display something about the client.
Now watch what happens on the first shell. The display will include something like 'listening on socket 12'.
On the second shell, start Tarantool and say:
box.cfg{}require('socket').tcp_connect('127.0.0.1', 3302)
Now watch what happens on the first shell. The display will include something like 'accepted connection from host: 127.0.0.1 family: AF_INET port: 37186'.