Recipes IO - tsafin/tarantool GitHub Wiki

I/O

Connect to a remote host by TCP

Use :func:`socket.tcp_connect()`

tarantool> s = require('socket').tcp_connect('google.com', 80)
tarantool> s:peer()
---
- host: 2a00:1450:4010:c02::8a -- IPv6 ready
  family: AF_INET6
  type: SOCK_STREAM
  protocol: tcp
  port: 80
  ...
tarantool> s:write("GET / HTTP/1.0\r\n\r\n")
---
 - 18
...

tarantool> s:read('\r\n') -- read until \r\n
---
 - "HTTP/1.0 302 Found\r\n"
...

tarantool> s:read('\r\n')
---
 - "Cache-Control: private\r\n"
...

All network I/O in Tarantool are asynchronous and non-blocking.

Simple TCP echo server

#!/usr/bin/env tarantool

local function handler(s, peer)
    s:write("Welcome to test server, " .. peer.host .."\n")
    while true do
        local line = s:read('\n')
        if line == nil then
            break -- error or eof
        end
        if not s:write("pong: "..line) then
            break -- error or eof
        end
    end
end

local server, addr = require('socket').tcp_server('localhost', 3311, handler)

Please not that :func:`socket.tcp_server()` handles each client in a separate fiber:

tarantool> require('fiber').info()
---
- 101:
    fid: 101
    name: tcpserver.lua # your script
    csw: 5
  103:
    fid: 103
    name: server/::1:37157 # client 1
    csw: 2
  104:
    fid: 104
    name: server/::1:37158 # client 2
    csw: 10
  102: -- server
    fid: 102
    name: server/::1:3311 # acceptor
    csw: 4
    ...

We tested 100k simultaneous clients on the single core of regular laptop.

Non-blocking DNS resolving

Use :func:`socket.getaddrinfo()`:

tarantool> require('socket').getaddrinfo('google.com', 'http', { type = 'SOCK_STREAM' })
---
- - host: 173.194.122.233
    family: AF_INET
    type: SOCK_STREAM
    protocol: tcp
  - host: 2a00:1450:4010:c04::8b
    family: AF_INET6
    type: SOCK_STREAM
    protocol: tcp
    port: 80

For tcp connections just use :func:`socket.tcp_connect`, which performs :func:`getaddrinfo` under the hood and then tries to connect to the first available address.

Simple UDP echo server

Tarantool currently don't have :func:`socket.udp_server()`, but this function can be implemented using sockets and fibers:

#!/usr/bin/env tarantool

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

local function udp_server_loop(s, handler)
    fiber.name("udp_server")
    while true do
        -- try to read a datagram first
        local msg, peer = s:recvfrom()
        if msg == "" then
            -- socket was closed via s:close()
            break
        elseif msg ~= nil then
            -- got a new datagram
            handler(s, peer, msg)
        else
            if s:errno() == errno.EAGAIN or s:errno() == errno.EINTR then
                -- socket is not ready
                s:readable() -- yield, epoll will wake us when new data arrived
            else
                -- socket error
                local msg = s:error()
                s:close() -- save resources and don't wait GC
                error("Socket error: " .. msg)
            end
        end
    end
end

local function udp_server(host, port, handler)
    local s = socket('AF_INET', 'SOCK_DGRAM', 0)
    if not s then
        return nil -- check errno:strerror()
    end
    if not s:bind(host, port) then
        local e = s:errno() -- save errno
        s:close()
        errno(e) -- restore errno
        return nil -- check errno:strerror()
    end

    fiber.create(udp_server_loop, s, handler) -- start a new background fiber
    return s
end

Usage:

local function handler(s, peer, msg)
    -- You don't have to wait until socket is ready to sent UDP
    -- s:writable()
    s:sendto(peer.host, peer.port, "Pong: " .. msg)
end

local server = udp_server('127.0.0.1', 3548, handler)
if not server then
    error('Failed to bind: ' .. errno.strerror())
end

print('Started')

require('console').start()

The example looks a little bit complicated because it actually check socket errors.

Write to a file

local fio = require('fio')
local errno = require('errno')
local f = fio.open('/tmp/xxxx.txt', {'O_CREAT', 'O_WRONLY', 'O_APPEND'},
    tonumber('0666', 8))
if not f then
    error("Failed to open file: "..errno.strerror())
end
f:write("Hello\n");
f:close()

Disk operations are asynchronous and non-blocking.

Read from a file

local fio = require('fio')
local errno = require('errno')
local f = fio.open('/tmp/xxxx.txt', {'O_RDONLY' })
if not f then
    error("Failed to open file: "..errno.strerror())
end
local data = f:read(4096)
f:close()
print(data)

Disk operations are asynchronous and non-blocking.

⚠️ **GitHub.com Fallback** ⚠️