2012-10-15 20 views
9

Nell'attuale implementazione di lua socket, vedo che dobbiamo installare un timer che richiama periodicamente in modo da controllare un'API non bloccante per vedere se abbiamo ricevuto qualcosa.prese Lua - eventi asincroni

Questo è tutto buono e bene tuttavia nel caso UDP, se il mittente ha un sacco di informazioni inviate, rischiamo di perdere i dati. Supponiamo che un altro dispositivo invii una foto da 2 MB tramite UDP e controlliamo che il socket riceve ogni 100msec. A 2MBps, il sistema sottostante deve memorizzare 200Kbits prima che la nostra chiamata interroghi lo stack TCP sottostante.

C'è un modo per far scattare un evento quando riceviamo i dati sul socket specifico anziché il polling che dobbiamo fare ora?

risposta

13

Ci sono vari modi di gestire questo problema; quale sceglierai dipende da quanto lavoro vuoi fare. *

Ma prima, è necessario chiarire (a se stessi) se si ha a che fare con UDP o TCP; non esiste uno "stack TCP sottostante" per i socket UDP. Inoltre, UDP è il protocollo sbagliato da utilizzare per inviare interi dati come un testo o una foto; si tratta di un protocollo inaffidabile, pertanto non si garantisce la ricezione di tutti i pacchetti, a meno che non si utilizzi una libreria socket gestita (come ENet).

Lua51/LuaJIT + LuaSocket

Polling è l'unico metodo.

  • Blocco: chiamare socket.select senza argomento orario e attendere che il socket sia leggibile.
  • Non bloccante: chiamare socket.select con un argomento di timeout di 0 e utilizzare sock:settimeout(0) sul socket da cui si sta leggendo.

Quindi chiamarli ripetutamente. Suggerirei di utilizzare un coroutine scheduler per la versione non bloccante, per consentire ad altre parti del programma di continuare l'esecuzione senza causare troppo ritardo.

Lua51/LuaJIT + LuaSocket + Lua Lanes (positiva)

Uguale al metodo di cui sopra, ma la presa esiste in un'altra corsia (un peso leggero stato Lua in un altro thread) effettuata utilizzando Lua Lanes (latest source). Ciò consente di leggere immediatamente i dati dal socket e in un buffer. Quindi, si utilizza un linda per inviare i dati al thread principale per l'elaborazione.

Questa è probabilmente la soluzione migliore per il tuo problema.

Ho fatto un semplice esempio di questo, disponibile here. Esso si basa su Lua Lanes 3.4.0 (GitHub repo) e un LuaSocket patch 2.0.2 (source, patch, blog post re' patch)

I risultati sono promettenti, anche se si dovrebbe assolutamente refactoring il mio codice di esempio, se si deriva da esso.

LuaJIT + specifici del sistema operativo prese

Se sei un po 'masochista, si può provare l'attuazione di una libreria presa da zero. FFI library di LuaJIT rende questo possibile dalla pura Lua. Lua Lanes sarebbe utile anche per questo.

Per Windows, suggerisco di dare un'occhiata a William Adam's blog. Ha avuto avventure molto interessanti con lo sviluppo di LuaJIT e Windows. Per quanto riguarda Linux e il resto, guarda i tutorial per C o la fonte di LuaSocket e traducili in operazioni LuaJIT FFI.

(LuaJIT supporta callbacks se l'API lo richiede, tuttavia, v'è un costo delle prestazioni signficant rispetto al polling da Lua a C.)

LuaJIT + ENet

ENet è una grande biblioteca. Fornisce il perfetto mix tra TCP e UDP: affidabile quando desiderato, altrimenti inaffidabile. Estrae anche dettagli specifici del sistema operativo, proprio come fa LuaSocket. È possibile utilizzare l'API Lua per collegarlo o accedervi direttamente tramite FFI di LuaJIT (consigliato).

* Punzo involontario.

3

Lua è intrinsecamente a thread singolo; non esiste un "evento". Non c'è modo di interrompere l'esecuzione del codice Lua. Quindi, mentre potevi montare qualcosa che sembrava un evento, ne avresti sempre avuto uno solo se avessi chiamato una funzione che interrogava quali eventi erano disponibili.

In genere, se si sta tentando di utilizzare Lua per questo tipo di lavoro di basso livello, si utilizza lo strumento sbagliato. Dovresti usare C o qualcosa per accedere a questo tipo di dati, quindi passarlo a Lua quando è pronto.

+1

Mi rendo conto che Lua è effettivamente un sistema a thread singolo con eventi di sistema che utilizzano i callback. Speravo solo di ricevere una chiamata quando ci sono dati disponibili invece di continuare a guardarli. – user4749

+0

Dovresti comunque trasferire il controllo a chiunque stia facendo la richiamata per ricevere la richiamata. Quindi ogni volta che lo faresti, controlla se i dati sono lì. –

+0

Non sono d'accordo molto. L'uso di lua-ev o di un altro evento NON impone il polling!Usando Lua + "Some Event Loop" viene utilizzato in modo altamente produttivo in dispositivi embedded (dove node.js è troppo grande) per attività serie con prestazioni molto buone e basso footprint. Per alcune situazioni questa è una corrispondenza perfetta. Ci sono moduli attorno ai quali è molto facile lavorare con il sistema operativo su un livello molto basso (https://github.com/justincormack/ljsyscall). – lipp

1

Probabilmente si sta utilizzando un select() non bloccante per i socket "poll" per tutti i nuovi dati disponibili. Luasocket non fornisce alcuna altra interfaccia per vedere se ci sono nuovi dati disponibili (per quanto ne so), ma se sei preoccupato che ci voglia troppo tempo quando lo fai 10 volte al secondo, considera di scrivere una versione semplificata che controlla solo una presa di cui hai bisogno ed evita di creare e buttare via i tavoli Lua. Se questo non è un'opzione, prendere in considerazione il passaggio nil-select() invece di {} per tali elenchi non c'è bisogno di leggere e passare le tabelle statiche invece di quelli temporanei:

local rset = {socket} 
... later 
...select(rset, nil, 0) 

invece di

...select({socket}, {}, 0) 
+0

Ok .. Questa è una buona idea e potrebbe funzionare molto bene – user4749

5

Io uso lua-ev https://github.com/brimworks/lua-ev per tutte le cose di multiplexing IO. È molto facile da usare, si adatta a Lua (e al suo function) come a un incantesimo. Si tratta di selezionare/poll/epoll o kqueue e funziona anche molto bene.

local ev = require'ev' 
local loop = ev.Loop.default 
local udp_sock -- your udp socket instance 
udp_sock:settimeout(0) -- make non blocking 
local udp_receive_io = ev.IO.new(function(io,loop) 
     local chunk,err = udp_sock:receive(4096) 
     if chunk and not err then 
      -- process data 
     end 
    end,udp_sock:getfd(),ev.READ) 

udp_receive_io:start(loop) 
loop:loop() -- blocks forever 

A mio parere Lua + luasocket + lua-EV è solo un dream team per la creazione di applicazioni di rete efficienti e robusti (per i dispositivi embedded/ambienti). Ci sono strumenti più potenti là fuori! Ma se le tue risorse sono limitate, Lua è una buona scelta!

+0

Potrebbe essere utilizzato su iPhone/Android con Gideros? (www.giderosmobile.com) Non ho visto una porta iPhone/Android. – user4749

+0

Scusa, non so se è possibile estendere i moduli Lua con associazioni C "native" nel mondo di gideros/corona. lua-ev (che è basato su libev) compila per "qualsiasi" Unix. – lipp