Aggiornamento: la risposta di Mr. Nemo ha aiutato a risolvere il problema! Il codice qui sotto contiene la correzione! Vedere le chiamate nb False
e nb True
di seguito.Utilizzo della chiamata di sistema GNU/Linux `splice` per copia da zero a socket Socket data transfer in Haskell
C'è anche un nuovo pacchetto Haskell chiamato splice
(che ha implementazioni OS-specifici e portatili di presa più noto per presa trasferimento dati loops) .
Ho il seguente (Haskell) Codice:
#ifdef LINUX_SPLICE
#include <fcntl.h>
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
#endif
module Network.Socket.Splice (
Length
, zeroCopy
, splice
#ifdef LINUX_SPLICE
, c_splice
#endif
) where
import Data.Word
import Foreign.Ptr
import Network.Socket
import Control.Monad
import Control.Exception
import System.Posix.Types
import System.Posix.IO
#ifdef LINUX_SPLICE
import Data.Int
import Data.Bits
import Unsafe.Coerce
import Foreign.C.Types
import Foreign.C.Error
import System.Posix.Internals
#else
import System.IO
import Foreign.Marshal.Alloc
#endif
zeroCopy :: Bool
zeroCopy =
#ifdef LINUX_SPLICE
True
#else
False
#endif
type Length =
#ifdef LINUX_SPLICE
(#type size_t)
#else
Int
#endif
-- | The 'splice' function pipes data from
-- one socket to another in a loop.
-- On Linux this happens in kernel space with
-- zero copying between kernel and user spaces.
-- On other operating systems, a portable
-- implementation utilizes a user space buffer
-- allocated with 'mallocBytes'; 'hGetBufSome'
-- and 'hPut' are then used to avoid repeated
-- tiny allocations as would happen with 'recv'
-- 'sendAll' calls from the 'bytestring' package.
splice :: Length -> Socket -> Socket -> IO()
splice l (MkSocket x _ _ _ _) (MkSocket y _ _ _ _) = do
let e = error "splice ended"
#ifdef LINUX_SPLICE
(r,w) <- createPipe
print ('+',r,w)
let s = Fd x -- source
let t = Fd y -- target
let c = throwErrnoIfMinus1 "Network.Socket.Splice.splice"
let u = unsafeCoerce :: (#type ssize_t) -> (#type size_t)
let fs = sPLICE_F_MOVE .|. sPLICE_F_MORE
let nb v = do setNonBlockingFD x v
setNonBlockingFD y v
nb False
finally
(forever $ do
b <- c $ c_splice s nullPtr w nullPtr l fs
if b > 0
then c_splice r nullPtr t nullPtr (u b) fs)
else e
(do closeFd r
closeFd w
nb True
print ('-',r,w))
#else
-- ..
#endif
#ifdef LINUX_SPLICE
-- SPLICE
-- fcntl.h
-- ssize_t splice(
-- int fd_in,
-- loff_t* off_in,
-- int fd_out,
-- loff_t* off_out,
-- size_t len,
-- unsigned int flags
--);
foreign import ccall "splice"
c_splice
:: Fd
-> Ptr (#type loff_t)
-> Fd
-> Ptr (#type loff_t)
-> (#type size_t)
-> Word
-> IO (#type ssize_t)
sPLICE_F_MOVE :: Word
sPLICE_F_MOVE = (#const "SPLICE_F_MOVE")
sPLICE_F_MORE :: Word
sPLICE_F_MORE = (#const "SPLICE_F_MORE")
#endif
Nota:Il codice di cui sopra ora funziona! Qui sotto non è più valido grazie a Nemo!
chiamo splice
come sopra definito con due socket aperti e connessi (che sono già utilizzati per trasmettere quantità minima di dati handshake utilizzando sia il socket API send
e recv
chiamate o convertiti in maniglie e utilizzati con hGetLine
e hPut
) e Continuo a ricevere:
Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)
al primo sito c_splice
chiamata: c_splice
rendimenti -1
e imposta alcune errno
ad un valore (probabilmente EAGAIN
) che legge resource exhausted | resource temporarily unavailable
wh it alzò gli occhi.
Ho provato a chiamare splice
con diversi valori Length
: 1024
, 8192
.
La versione corrente crea una nuova pipe ogni volta che si chiama splice(). Questo è OK se si spostano sempre blocchi di grandi dimensioni, ma per piccoli blocchi che possono imporre un grande sovraccarico. Generalmente creo un oggetto "Splicer" per possedere il pipe, quindi lo chiamo ripetutamente da + a descrittori per spostare i dati. – Nemo
@Nemo 'splice' (non' c_splice') è in realtà un ciclo infinito dovuto a 'per sempre'. Credo che dovrei rinominare 'splice' in qualcosa come' loopSplice' per renderlo chiaro. Così attualmente crea una pipe per ogni connessione proxy non per ogni chiamata 'c_splice'. –
Ho ancora molto da testare con l'implementazione portatile su Windows, quindi avrò sicuramente abbastanza tempo per pensare a un nome migliore. Aperto anche per i tuoi suggerimenti :) –