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:

Ну, я редактирую это на будущее :)

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

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

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


person Cyclonus    schedule 04.11.2012    source источник


Ответы (2)


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

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

person Paul Kulchenko    schedule 04.11.2012
comment
Спасибо за ссылки, я думаю, что я, вероятно, в конечном итоге буду работать с сопрограммами (и буду изучать модель диспетчера из вашего примера) для этого, но если я смогу заставить вещи работать с дорожками, это кажется более ... элегантным? Если это важная часть: P - person Cyclonus; 05.11.2012

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

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

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

Решения!

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

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

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

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

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

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

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

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

Это, наверное, самый подходящий вариант для вас.

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

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

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

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

LuaJIT + ENet

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

person Deco    schedule 04.11.2012
comment
Deco: К вашему сведению, ваша ссылка в параграфе "Решения" включает / редактирует ее. - person Paul Kulchenko; 05.11.2012
comment
Спасибо за комплексное решение :) Я пытаюсь выбрать решение для двух- или многопоточного опроса, как вы описали, но у меня все еще возникают проблемы с тем, чтобы полосы работали для меня. (Также спасибо, что указали, что мне понадобится сокет внутри переулка, я не знал об этом). Я отредактирую свой первоначальный вопрос, чтобы опубликовать пример того, что я делаю и как это не удается. Если у вас будет время взглянуть, может быть, я что-то упускаю? Спасибо - person Cyclonus; 05.11.2012