Версия:

Модуль socket

Модуль socket

Общие сведения

Модуль socket позволяет обмениваться данными с локальным или удаленным хостом по BSD-сокетам в режиме с установлением соединений (TCP) или на основе датаграмм (UDP). Семантика вызовов в API модуля socket точно соответствует семантике соответствующих вызовов в POSIX. Имена и сигнатуры функций по большей части совместимы с luasocket.

Функции для настройки и подключения: socket, sysconnect, tcp_connect. Функции для отправки данных: send, sendto, write, syswrite. Функции для получения данных: recv, recvfrom, read. Функции для ожидания отправки/получения данных: wait, readable, writable. Функции для установки флагов: nonblock, setsockopt. Функции для остановки и отключения: shutdown, close. Функции для проверки ошибок: errno, error.

Указатель

Ниже приведен перечень всех функций модуля socket.

Имя Назначение
socket() Создание сокета
socket.tcp_connect() Подключение к удаленному хосту с помощью сокета
socket.getaddrinfo() Получение информации об удаленном узле
socket.tcp_server() Использование Tarantool’а в качестве TCP-сервера
socket_object:sysconnect() Подключение к удаленному хосту с помощью сокета
socket_object:send()
socket_object:write()
Отправка данных по подключенному сокету
socket_object:syswrite() Запись данных в буфер сокета без блокировки
socket_object:recv() Чтение с подключенного сокета
socket_object:sysread() Чтение данных из буфера сокета без блокировки
socket_object:bind() Привязка сокета к данному хосту/порту
socket_object:listen() Начало прослушивания входящих соединений
socket_object:accept() Принятие запроса клиента на соединение + создание подключенного сокета
socket_object:sendto() Отправка сообщения по UDP-сокету на указанный хост
socket_object:recvfrom() Получение сообщения по UDP-сокету
socket_object:shutdown() Отключение передачи данных на чтение, на запись или в обоих направлениях
socket_object:close() Закрытие сокета
socket_object:error()
socket_object:errno()
Получение информации о последней ошибке на сокете
socket_object:setsockopt() Определение флагов сокета
socket_object:getsockopt() Получение флагов сокета
socket_object:linger() Установить/убрать флаг SO_LINGER
socket_object:nonblock() Определить/получить значение флага
socket_object:readable() Ожидание доступности чего-либо для чтения
socket_object:writable() Ожидание доступности чего-либо для записи
socket_object:wait() Ожидание доступности чего-либо для чтения или записи
socket_object:name() Получение информации о ближней стороне соединения
socket_object:peer() Получение информации о дальней стороне соединения
socket.iowait() Ожидание активности чтения/записи

Как правило, сессия сокета начинается с функций настройки, определяет один или более флагов, запустит цикл с функциями отправки и получения и закончится функциями завершения – как в примере в конце данного раздела. В течение сессии может быть проверка на ошибки и ожидание синхронизации функции. Чтобы файбер с сокетом не блокировал другие файберы, правила неявной передачи управления заставят его передать управление другим процессам в рамках кооперативной многозадачности.

Для всех примеров в данном разделе имя сокета будет sock, а вызов функции будет выглядеть как sock:имя_функции(...).

socket.__call(domain, type, protocol)

Создание нового TCP-сокета или UDP-сокета. Значения аргумента остаются теми же, что и на странице socket(2) руководства по Linux.

возвращает:неподключенный сокет или nil.
тип возвращаемого значения:
 пользовательские данные

Пример:

socket('AF_INET', 'SOCK_STREAM', 'tcp')
socket.tcp_connect(host[, port[, timeout]])

Подключение к удаленному хосту с помощью сокета.

Параметры:
  • host (string) – URL или IP-адрес
  • port (number) – номер порта
  • timeout (number) – время ожидания
возвращает:

подключенный сокет, если нет ошибки.

тип возвращаемого значения:
 

пользовательские данные

Пример:

socket.tcp_connect('127.0.0.1', 3301)
socket.getaddrinfo(host, type[, {option-list}])

Функция socket.getaddrinfo() используется для поиска информации об удаленном узле, чтобы можно было передать правильные аргументы для sock:sysconnect(). Эта функция может использовать конфигурационный параметр worker_pool_threads.

возвращает:Таблица со следующими полями: «host», «family», «type», «protocol», «port».
тип возвращаемого значения:
 таблица

Пример:

tarantool> socket.getaddrinfo('tarantool.org', 'http')
---
- - host: 188.93.56.70
    family: AF_INET
    type: SOCK_STREAM
    protocol: tcp
    port: 80
  - host: 188.93.56.70
    family: AF_INET
    type: SOCK_DGRAM
    protocol: udp
    port: 80
...
socket.tcp_server(host, port, handler-function-or-table[, timeout])

Функция socket.tcp_server() заставляет Tarantool выступать в качестве сервера для принятия подключений. Обычно для этой же цели используется box.cfg{listen=…}.

Параметры:
  • host (string) – имя или IP хоста
  • port (number) – порт хоста, может быть 0
  • handler-function-or-table (function/table) – что выполнить после подключения
  • timeout (number) – количество секунд ожидания

Параметр handler-function-or-table может представлять собой просто имя функции или объявление функции: handler_function. Или же может быть таблицей: {handler = handler_function [, prepare = prepare_function] [, name = name] }. Функция handler_function является обязательной, в ней может быть только один параметр = сокет (используется для непрерывной работы после установки соединения). Функция prepare_function необязательна; она выполняется однократно перед установкой соединения. Например:

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})

Более полный пример см. в разделе Использование tcp_server для получения содержимого файла, отправленного по socat.

object socket_object
socket_object:sysconnect(host, port)

Подключение к удаленному хосту с помощью существующего сокета. Значения аргументов будут такие же, как в tcp_connect(). Хост должен представлять собой IP-адрес.

Параметры:
  • Либо:
    • host – строковое представление IPv4 адреса или IPv6 адреса;
    • port – число.
  • Либо:
    • host – строка, которая содержит «unix/»;
    • port – строка, которая содержит путь к Unix-сокету.
  • Либо:
    • host – число, 0 (ноль), что означает «все локальные интерфейсы»;
    • port – число. Если номер порта – 0 (ноль), сокет будет привязан к случайному локальному порту.
возвращает:значение объекта сокета может изменяться, если будет выполнена функция sysconnect().
тип возвращаемого значения:
 boolean (логический)

Пример:

socket = require('socket')
sock = socket('AF_INET', 'SOCK_STREAM', 'tcp')
sock:sysconnect(0, 3301)
socket_object:send(data)
socket_object:write(data)

Отправка данных по подключенному сокету.

Параметры:
  • data (string) – что отправляется
возвращает:

количество отправляемых байтов.

тип возвращаемого значения:
 

число

Возможные ошибки: nil в случае ошибки.

socket_object:syswrite(size)

Запись максимально возможного количества данных в буфер сокета без блокировки. Используется редко. Для получения подробной информации см. описание по ссылке this description.

socket_object:recv(size)

Чтение количества байтов, определенного в size, из подключенного сокета. Внутренний буфер опережающего считывания используется для уменьшения использования ресурсов на вызов.

Параметры:
возвращает:

строка запрошенной длины, если выполнено.

тип возвращаемого значения:
 

строка

Возможные ошибки: В случае ошибки возвращается пустая строка, после чего статус, errno, errstr. Если передача данных на запись закрыта с другой стороны, возвращаются оставшиеся для чтения данные из сокета (возможно, пустая строка), после чего идет статус «eof» (конец файла).

socket_object:read(limit[, timeout])
socket_object:read(delimiter[, timeout])
socket_object:read({options}[, timeout])

Чтение данных из подключенного сокета до выполнения какого-либо условия и возврат прочтенных байтов. Производится чтения количества байтов, которое указано в параметре limit, либо до символа-разделителя, либо до истечения времени ожидания. В отличие от socket_object:recv (где используется внутренний буфер опережающего считывания), socket_object:read зависит от буфера сокета.

Параметры:
  • limit (integer) – максимальное количество байтов для чтения, например, 50 означает «остановиться на 50 байтах»
  • delimiter (string) – разделитель, например, „?“ означает «остановиться после знака вопроса»
  • timeout (number) – максимальное количество секунд ожидания, например, 50 означает «остановиться через 50 секунд».
  • options (table) – chunk=предел и/или delimiter=разделитель, например, {chunk=5,delimiter='x'}.
возвращает:

пустая строка, если нет данных для чтения, либо нулевое значение nil в случае ошибки, либо строка, ограниченная количеством байтов в limit, которая может включать в себя байты, совпадающие с выражением delimiter.

тип возвращаемого значения:
 

строка

socket_object:sysread(size)

Возврат данных из буфера сокета без блокировки.Если сокет с блокировкой, sysread() может блокировать процесс вызова. Используется редко. Для получения подробной информации, см. описание.

Параметры:
  • size (integer) – максимальное количество байтов для чтения, например, 50 означает «остановиться на 50 байтах»
возвращает:

пустая строка, если нет данных для чтения, либо нулевое значение nil в случае ошибки, либо строка, ограниченная количеством байтов в size.

тип возвращаемого значения:
 

строка

socket_object:bind(host[, port])

Привязка сокета к данному хосту/порту. UDP-сокет после привязки может использоваться для получения данных (см. socket_object.recvfrom). TCP-сокет может использоваться для принятия новых соединений после перевода в режим прослушивания.

Параметры:
  • host (string) – URL или IP-адрес
  • port (number) – номер порта
возвращает:

true (правда), если выполнено, false (ложь) в случае ошибки. Если возвращается false, используйте socket_object:errno() или socket_object:error() для получения подробной информации.

тип возвращаемого значения:
 

boolean (логический)

socket_object:listen(backlog)

Начало прослушивания входящих соединений.

Параметры:
  • backlog – в Linux очередь запросов backlog может быть в /proc/sys/net/core/somaxconn, в BSD очередь запросов может представлять собой SOMAXCONN.
возвращает:

true (правда), если выполнено, false (ложь) в случае ошибки.

тип возвращаемого значения:
 

boolean (логический).

socket_object:accept()

Принятие нового клиентского соединения и создание нового подключенного сокета. Установка блокирующего режима на сокете явным образом после принятия соединения приведет к эффективной работе.

возвращает:новый сокет, если выполнено.
тип возвращаемого значения:
 пользовательские данные

Возможные ошибки: nil.

socket_object:sendto(host, port, data)

Отправка сообщения по UDP-сокету на указанный хост.

Параметры:
  • host (string) – URL или IP-адрес
  • port (number) – номер порта
  • data (string) – что отправляется
возвращает:

количество отправляемых байтов.

тип возвращаемого значения:
 

число

Возможные ошибки: в случае ошибки возвращает nil, а также может вернуть статус, errno, errstr.

socket_object:recvfrom(size)

Получение сообщения по UDP-сокету.

Параметры:
возвращает:

сообщение, таблица с полями «host», «family» и «port».

тип возвращаемого значения:
 

строка, таблица

Возможные ошибки: в случае ошибки возвращает nil, статус, errno, errstr.

Пример:

После message_content, message_sender = recvfrom(1) значением message_content может быть строка, которая содержит „X“, а значением message_sender может быть таблица, которая содержит

message_sender.host = '18.44.0.1'
message_sender.family = 'AF_INET'
message_sender.port = 43065
socket_object:shutdown(how)

Отключение передачи данных на чтение, на запись или в обоих направлениях.

Параметры:
  • how – socket.SHUT_RD, socket.SHUT_WR, or socket.SHUT_RDWR.
возвращает:

true (правда) или false (ложь).

тип возвращаемого значения:
 

boolean (логический)

socket_object:close()

Закрытие (удаление) сокета. Закрытый сокет больше не должен использоваться. Сокет будет закрыт автоматически, когда сборщик мусора Lua удалит данные.

возвращает:true (правда), если выполнено, false (ложь) в случае ошибки. Например, если сокет sock уже закрыт, sock:close() вернет false.
тип возвращаемого значения:
 boolean (логический)
socket_object:error()
socket_object:errno()

Получение информации о последней ошибке на сокете, если таковая была. Ошибки не выдают исключения, поэтому данные функции необходимы.

возвращает:результат sock:errno(), результат sock:error(). Если ошибки нет, то sock:errno() вернет 0 и sock:error().
тип возвращаемого значения:
 число, строка
socket_object:setsockopt(level, name, value)

Определение флагов сокета. Значения аргумента будут такими же, что и на странице getsockopt(2) руководства по Linux. Tarantool принимает следующие:

  • 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

Установка флага SO_LINGER осуществляется с помощью sock:linger(active).

socket_object:getsockopt(level, name)

Получение флагов сокета. Список возможных флагов см. с помощью sock:setsockopt().

socket_object:linger([active])

Установить или убрать флаг SO_LINGER. Описание флага см. в руководстве по Linux.

Параметры:
  • active (boolean) –
возвращает:

новые значения active и timeout.

socket_object:nonblock([flag])
  • sock:nonblock() возвращает текущее значение флага.
  • sock:nonblock(false) устанавливает флаг на false и возвращает false.
  • sock:nonblock(true) устанавливает флаг на true и возвращает true.

Эту функцию можно использовать до вызова функции, которая в противном случае будет блокировать бесконечно.

socket_object:readable([timeout])

Ожидание доступности чего-либо для чтения или до истечения времени ожидания.

возвращает:true, если сокет доступен для чтения, false, если истекло время ожидания;
socket_object:writable([timeout])

Ожидание доступности чего-либо для записи или до истечения времени ожидания.

возвращает:true, если сокет доступен для записи, false, если истекло время ожидания;
socket_object:wait([timeout])

Ожидание доступности чего-либо для чтения или записи, или до истечения времени ожидания.

возвращает:„R“, если сокет доступен для чтения, „W“, если сокет доступен для записи, „RW“, если сокет доступен и для чтения, и для записи, „“ (пустая строка), если истекло время ожидания;
socket_object:name()

Функция sock:name() используется для получения информации о ближней стороне соединения. Если сокет привязан к xyz.com:45, то sock:name вернет информацию о [host:xyz.com, port:45]. Аналогичная функция в POSIX – getsockname().

возвращает:Таблица со следующими полями: «host», «family», «type», «protocol», «port».
тип возвращаемого значения:
 таблица
socket_object:peer()

Функция sock:peer() используется для получения информации о дальней стороне соединения. Если TCP-соединение установлено с удаленным хостом tarantool.org:80, то sock:peer() вернет информацию о [host:tarantool.org, port:80]. Аналогичная функция в POSIX – getpeername().

возвращает:Таблица со следующими полями: «host», «family», «type», «protocol», «port».
тип возвращаемого значения:
 таблица
socket.iowait(fd, read-or-write-flags[, timeout])

Функция socket.iowait() используется для ожидания, пока дескриптор файла не будет активен для чтения или записи.

Параметры:
  • fd – дескриптор файла
  • read-or-write-flags – „R“ или 1 = чтение, „W“ или 2 = запись, „RW“ или 3 = чтение|запись.
  • timeout – количество секунд ожидания

Если значение параметра fd – nil, то будет режим ожидания до истечения времени, указанного в параметре timeout. Если timeout – nil или не указан, время ожидания считается бесконечным.

Как правило, возвращается значение совершенного действия („R“ или „W“, или „RW“, или 1, или 2, или 3). Если время ожидания в timeout проходит без действий чтения или записи, возвращается ошибка = ETIMEDOUT.

Пример: socket.iowait(sock:fd(), 'r', 1.11)

Примеры

Использование TCP-сокета в интернете

В данном примере устанавливается соединение по интернету между экземпляром Tarantool’а и tarantool.org, затем отправляется HTTP-сообщение заголовка «head» и возвращается ответ: «HTTP/1.1 200 OK» или что-то другое, если сайт перемещен. Так не слишком удобно взаимодействовать с определенным сайтом, но пример показывает работу системы.

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
...

Использование UDP-сокета на localhost

Ниже приведен пример с датаграммами. Устанавливается два соединения с 127.0.0.1 (localhost): sock_1 и sock_2. С помощью sock_2 отправляется сообщение на sock_1. С помощью sock_1 получается сообщение. Отображается полученное сообщение. Оба соединения закрываются.
Компьютеру так не слишком удобно взаимодействовать с самим собой, но пример показывает работу системы.

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
...

Использование tcp_server для получения содержимого файла, отправленного по socat

Ниже приведен пример функции tcp_server, которая читает строки с клиента и выводит результат. На клиентской стороне утилита socat в Linux будет использоваться для отправки целого файла на чтение функции tcp_server.

Запустите две оболочки. Первая оболочка будет экземпляром сервера. Вторая оболочка будет клиентом.

В первой оболочке запустите Tarantool и выполните:

box.cfg{}
socket = require('socket')
socket.tcp_server('0.0.0.0', 3302,
{
  handler = function(s)
    while true do
      local request
      request = s:read("\n");
      if request == "" or request == nil then
        break
      end
      print(request)
    end
  end,
  prepare = function()
    print('Initialized')
  end
}
)

Вышеуказанный код означает: использовать tcp_server() для ожидания подключения с любого хоста по порту 3302. Когда это произойдет, ввести цикл, который читает по сокету и выводит результат чтения. Разделителем для функции чтения будет «\n», поэтому каждое выполнение read() выполнит чтение строки до перевода строки, включая перевод строки.

Во второй оболочке создайте файл, который содержит несколько строк. Содержимое не имеет значения. Предположим, что первая строка содержит A, вторая строка содержит B, третья строка содержит C. Назовите этот файл «tmp.txt».

Во второй оболочке используйте утилиту socat для отправки файла tmp.txt на экземпляр сервера по хосту и порту:

$ socat TCP:localhost:3302 ./tmp.txt

Теперь смотрите, что происходит в первой оболочке. Выводятся строки «A», «B», «C».