LuaLanes и LuaSockets

Я работаю над небольшим приложением Lua (под Lua для Windows, если это имеет значение), которое использует сокеты для связи с внешним миром. (LuaSocket)

И я пытаюсь сделать несколько запросов параллельно. Поэтому я подумал, что LuaLanes - это путь. (Конечно, я открыт для альтернатив, если есть лучшее решение, но предпочел бы не иметь дело с сопрограммами для этого.)

Что-то вроде этого:

server = assert (socket.bind ('*', 1234))
client = server : accept ()
-- set id to some unique value
allClients [id] = client
theLane = lanes.gen ("", laneTest) ( id )
print (theLane [1])

Где laneTest Функция определяется следующим образом:

function laneTest (id)
    local client = allClients [id]
    print ('peer: ', client:getpeername())
end

Моя проблема в том, что внутри laneTest функция, при запуске в качестве полосы движения, я получаю это прекрасное сообщение об ошибке:

попытка индексировать локальный "клиент" (значение пользовательских данных)

(с линии client:getpeername())

Итак.. Я не уверен, что здесь происходит? Линии несовместимы с сокетами, или я делаю что-то очень неправильно?

Я предполагаю, что может быть, что версия переулков, которая поставляется с Lua для Windows, является древней ( luaforwindows) и не работает с сокетами, но последняя версия может? (Дорожки 2.0.4 и более поздняя версия 3.xx)

Я действительно не знаю, как перейти к обновлению имеющейся у меня версии Lanes, иначе я бы попробовал это сейчас. Буду признателен за любой совет, если это то, куда я мог бы пойти или есть что-то более очевидное, что я сделал не так

Редактировать: я пошел дальше и установил полосы через luarocks, и у меня возникла та же проблема с использованием полос 3.1.6-1, которые установлены в виде камня.

Изменить 2: пробовал это (и все еще не удалось):

require ('socket')
require ('lanes')
local allClients = {}

function theLane (id)
    print ('the id:', id) -- correctly prints out the id passed to the function
    local SOCKET = require ('socket')
    local client = allClients [id]
    print ('peer:', client:getpeername())
    client : close ()
end

local server = assert (SOCKET.bind ('*', 1234))
local ip, port = server:getsockname ()
local laneFunc = lanes.gen('', theLane)
local client = server:accept ()
allClients [1] = client
local x = laneFunc (1)
print (x[1])
  1. Это не утверждает: attempt to call global 'require' (a nil value)
  2. Удаление require ('socket') Строка внутри функции и повторная попытка также проваливаются, говоря: attempt to index local 'client' (a userdata value)

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

Изменить 3:

Ну, я редактирую это для дальнейшего использования:)

Насколько я могу судить, невозможно использовать Lanes с Sockets без исправления luasockets. Смотрите обсуждение здесь для получения дополнительной информации об этом; но вкратце (и как объяснено в ответе Деко): полосы не работают с пользовательскими данными. luasocket не предоставляет другого способа доступа к информации о сокетах / сокетах.

У меня нет желания исправлять luasocket так, как я бы предпочел использовать полосы, я собираюсь идти вперед и придерживаться Copas или Couroutines.

Спасибо всем!

2 ответа

Решение

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

Существует также библиотека copas, которая описывает себя как "диспетчер, основанный на сопрограммах, которые могут использоваться серверами TCP/IP", но вы также можете использовать ее для асинхронной отправки запросов (используя комбинацию addthread а также step звонки).

Lua Lanes создает совершенно новое (но минимальное) состояние Lua для каждой дорожки. Любые переданные значения или аргументы копируются, на них нет ссылок; это означает, что ваша таблица allClients копируется вместе с сокетами, которые она содержит.

Ошибка возникает из-за того, что сокеты являются userdata, которые Lua Lanes не знает, как копировать без каких-либо рекомендаций от модуля C. К сожалению, LuaSocket не дает таких советов. (Есть способы обойти это, но будьте осторожны: LuaSocket не является потокобезопасным, и ошибки синхронизации очень трудно отследить.)

Хотя это не решит вашу проблему, вы должны заметить, что вам нужно установить LuaSocket в вашем порожденном переулке; он не копируется по умолчанию.

Решения!

Они упорядочены от простого к сложному (и в основном транскрибируются из моего другого ответа здесь).

Однопоточный опрос

Повторно вызовите функции опроса в LuaSocket:

  • Блокировка: вызов socket.select без аргумента времени и ждать, пока сокет не будет читабельным.
  • Неблокирующая: звонок socket.select с аргументом тайм-аута 0и использовать sock:settimeout(0) на сокете вы читаете из.

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

(Если вы выберете это решение, я предлагаю рассмотреть ответ Пола.)

Двухпоточный опрос

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

Это, наверное, самый жизнеспособный вариант для вас.

Многопоточный опрос

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

Я сделал простой пример этого, доступный здесь. Он опирается на Lua Lanes 3.4.0 ( GitHub repo) и исправленный LuaSocket 2.0.2 ( источник, патч, пост в блоге 're' patch)

Результаты многообещающие, хотя вы должны определенно провести рефакторинг моего примера кода, если вы его извлекаете.

LuaJIT + ENet

ENet - отличная библиотека. Он обеспечивает идеальное сочетание TCP и UDP: надежный при желании, ненадежный в противном случае. Он также абстрагирует специфические детали операционной системы, так же, как это делает LuaSocket. Вы можете использовать Lua API, чтобы связать его, или напрямую получить к нему доступ через FFI LuaJIT (рекомендуется).

Другие вопросы по тегам