Update: Mr. Nemos Antwort half, das Problem zu lösen! Der folgende Code enthält den Fix! Siehe die folgenden Anrufe nb False
und nb True
.Verwendung von GNU/Linux-Systemaufruf `splice` für zero-copy Socket-Socket-Datenübertragungen in Haskell
Es gibt auch ein neues Paket Haskell genannt splice
(, das hat OS-spezifische und portable Implementierungen bekanntesten Buchse Datenübertragung an die Buchse Schleifen) .
Ich habe folgendes (Haskell) Code:
#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
Hinweis:Der obige Code jetzt funktioniert einfach! Unten ist nicht mehr gültig dank Nemo!
I splice
nennen, wie oben definiert, mit zwei offenen und verbundenen Buchsen (die bereits verwendet werden, eine minimale Menge an Handshake-Daten entweder die Buchsen API send
und recv
Anrufe oder umgewandelt Griffen und mit hGetLine
und hPut
verwendet verwendet zu übertragen), und ich erhalte:
Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)
am ersten c_splice
Aufrufort: c_splice
kehrt -1
und einige errno
auf einen Wert setzt (wahrscheinlich EAGAIN
), die resource exhausted | resource temporarily unavailable
liest wh en blickte auf.
Ich testete Aufruf splice
mit verschiedenen Length
Werte: 1024
, 8192
.
Ihre aktuelle Version erstellt bei jedem Aufruf von splice() eine neue Pipe. Dies ist in Ordnung, wenn Sie immer große Blöcke verschieben, aber für kleine Blöcke, die einen großen Aufwand verursachen können. Normalerweise erstelle ich ein "Splicer" -Objekt, um die Pipe zu besitzen, und rufe es dann wiederholt mit + von Deskriptoren auf, um die Daten zu verschieben. – Nemo
@Nemo 'splice' (nicht' c_splice') ist eigentlich eine Endlosschleife wegen 'forever'. Ich denke, ich sollte "splice" in etwas wie "loopSplice" umbenennen, um das klarzustellen. Daher erstellt es derzeit pro Pipe pro Proxy-Verbindung nicht pro 'c_splice'-Aufruf eine Pipe. –
Ich habe noch eine Menge zu testen mit der portablen Implementierung unter Windows, aber ich werde definitiv genug Zeit haben, um über einen besseren Namen nachzudenken. Öffnen Sie für Ihre Vorschläge auch :) –