sockets 1.6 - AnaNek/tarantool GitHub Wiki

box.errno

A list of error codes from (7) errno.h exported to Lua.

Methods:

  • box.errno() - last errno
  • box.errno.E* - error codes from (7) errno.h (for example, box.errno.EBUSY)
  • box.errno.strerror(code) - returns string error description based on error code

Domain Name Resolution


box.socket.getaddrinfo(host, port[, timeout][, hints ])

Returns an array with hosts, e.g.:

{
  { host = '127.0.0.1', port = 8080, family = 'AF_INET', type = 'SOCK_STREAM', protocol = 'tcp' },
  { host = '::1', port = 8080, family = 'AF_INET6', type = 'SOCK_STREAM', protocol = 'tcp' },
  ...
}

Optional hints argument is a Lua table with one of several of the hints below:

family - protocol family (a string such as 'AF_INET', 'AF_UNIX', etc)

type - socket type (string, 'SOCK_STREAM', 'SOCK_DGRAM', etc)

protocol - protocol name (string, 'tcp', 'ip', etc)

flags - a table with flags (strings in AI_* family)

Any value of the returned array can be used to create a socket.

In case of error, returns n nil. Use box.errno() to find out more about the occurred error.

Creating a socket


s = box.socket(domain, type, protocol)

Domain, type, protocol - string constants with names identical to one in man 2 socket and to the contents of /etc/protocols

Returns a socket object, or nil in case of error.

In case of error, an error code and a text description of the error are available with: socket:errno() and socket:error()

Example:


local s = socket('AF_INET', 'SOCK_STREAM', 'tcp')

s:errno(), s:error()

Error code and error message of the last error occurred on this socket.

s:sysconnect(host, port)

A low level binding to man 2 connect, which tries to connect to the given address . host, port - must be an IPv4, IPv6 address or a UNIX path.


  local s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
  if not s:sysconnect('127.0.0.1', 80) then
     error("Can't connect to server '127.0.0.1:80': " .. s:error())
  end
  s:write("HEAD / HTTP/1.1\r\nHost: mail.ru\r\nConnection: close\r\n\r\n")
  return s:readline({ "\r\n\r\n", "\n\n" })

s:sysread(count)

A low level read. Issues man 2 read under the hood. Returns a Lua string (possibly empty) with read data. In case of error, returns nil. Can block the calling process in case the socket is blocking.


local data = s:sysread(4096)
if data == nil then
	error(s:error())
end
buf = buf .. data

s:syswrite(string)

A low level write. Calls man 2 write under the hood. Returns the number of bytes written or nil in case of error. Can block the calling process if the socket is blocking.


local sent = s:syswrite('Hello, world')
if sent == nil then
	error(s:error())
end


s:nonblock([flag])

Get, set or clear the non-blocking socket flag. Returns the current value of the flag.

s:close()

Close the socket. Returns true in case of success or false in case of error.

s:shutdown(how)

Shut down the reading or writing end of the socket, or both. See man 2 shutdown. Returns true in case of success and false in case of error. how can be one of the following:

  • R or READ or 0 - close the reading end
  • W or WRITE or 1 - close the wtiting end
  • RW or READ_WRITE or 2 - close both the reading and the writing end.

s:sysbind(host, port)

Bind a socket to a host/port pair. The host and port must not require a domain name resolution, same as in :sysconnect. Returns true on success and false on error.

s:listen(backlog)

Begins listening on a socket. See also man 2 listen. Returns true on success and false on error.

s:accept()

Accepts an incoming connection. Returns a new socket or nil in case of error. Can block the calling process unless the socket is marked as non-blocking. Note that the new socket inherits the blocking mode from the acceptor socket on some operating system, and does not inherits the mode on others, so it is a good practice to set the mode explicitly on the accepted socket.

s:setsockopt(level, name, value) and s:getsockopt(level, name)

Set or clear socket flags (see man setsockopt and man getsockopt). Supports the following flags: SO_ACCEPTCONN, SO_BINDTODEVICE, SO_BROADCAST, SO_BSDCOMPAT, 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 or clearing SO_LINGER is done with so:linger(active, timeout)

s:readable([timeout])

Block the calling fiber until the socket becomes readable or the timeout expires. Returns true if/when the socket has data available for reading, and false in case of error or timeout.

s:writable([timeout])

Block the calling fiber until the socket becomes writable or the timeout expires. Returns true if/when the socket is available for reading, and false in case of error or timeout.

s:wait([timeout])

Block the calling fiber until the socket becomes readable or writable or the timeout expires. Returns string R, W, RW, describing the state of the socket, or nil in case of timeout.

s:read([len[, timeout]])

A wrapper around s:sysread(). Reads data from the socket until one of the following conditions is met:

  • the number of bytes read is at least len
  • eof is read from the socket (sysread returns 0 bytes)
  • timeout is reached
  • a read error occurred

Returns a string with data in cases of success or nil in case of error.

Notes:

  • the data is read into an internal buffer, s.rbuf
  • if there is data already in s.rbuf this data is taken into account in a subsequent call to s:read() and s:readline() (but not in s:sysread())
  • s:sysread() bypasses s.rbuf

s:readline([len], [eols], [timeout])

A wrapper around s:sysread().

Read data from the socket until one of the following conditions is met:

  • A string ending with one of the markers passed in eols table is read
  • eof is read (sysread() returns 0 bytes)
  • timeout is reached
  • length limit (passed in len) is reached
  • error occurred

Returns a string with data in case of success and `nil in case of error.

Uses s.rbuf along with s:read().

s:write(str[, timeout])

A wrapper around s:syswrite(). Write str to the socket, untl one of the following events occurs:

  • str is fully written
  • timeout is reached.
  • an error occurred.

Returns true on success and false on error.

s:recv(size[, flags]) and s:send(str[, flags])

Receive or send a datagramm. See man 3 recv и man 3 send. s:send() returns true on success and false on error. s:recv() returns a string with a datagram on success and nil in case of error.

Examples

Connect to a TCP server


function tcp_connect(host, port)
	local ainfo =
		 box.socket.getaddrinfo(host, port, nil, { protocol = 'tcp' })
	if ainfo == nil then
		error( box.errno.strerror(box.errno()) )
	end
	for i, a in pairs(ainfo) do
		local s = box.socket(a.family, a.type, a.protocol)
		if s == nil then
			error( box.errno.strerror(box.errno()) )
		end

		s:nonblock(true)

		if s:sysconnect(a.host, a.port) then
			return s
		end
	end

	error("Can't connect to " .. host .. ":" .. port)
end


A simple HTTP client


function http_request(host, port, request)
    local s = tcp_connect(host, port)
    s:write(request)
    local header = s:readline(4096, { "\r\n\r\n", "\n\n" })
    header = parse_header(header)
    local body = s:read(header['content-length'])
    return body, header
end

A simple Echo-server


local ms = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
ms:nonblock(true)
ms:bind('127.0.0.1', 8080)
ms:listen(128)
while ms:readable() do
  local client = ms:accept()
  client:nonblock(true)
  box.fiber.wrap(
    function(client)
      local request = client:readline(512, { "\n" })
      client:shutdown('READ')
      client:write(request)
      client:close()
    end,
    client)
end

ms:close()

A simple Echo-server (working)

local socket = require('socket')
local fiber = require('fiber')

local ms = socket('PF_INET', 'SOCK_STREAM', 'tcp')
ms:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)
ms:nonblock(true)
ms:bind('127.0.0.1', 8080)
ms:listen(128)
while ms:readable() do
  local client = ms:accept()
  client:nonblock(true)
  fiber.create(
    function(client)
      client:readable()
      local request = client:recv()
      client:shutdown('READ')
      client:write(request)
      client:close()
    end,
    client)
end

ms:close()

A high level TCP connect

socket.tcp_connect()


local socket = box.socket.tcp_connect(host, port, timeout)

This functions resolves the host and port, and establishes a connection. If host contains string unix/, then port must contain a path to a UNIX-socket.