Recipes IO - tsafin/tarantool GitHub Wiki
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.
#!/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.
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.
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.
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.
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.