업데이트 : Mr. Nemo의 대답이 문제를 해결하는 데 도움이되었습니다. 아래 코드에는 수정 사항이 포함되어 있습니다! 아래 전화 번호는 nb False
과 nb True
입니다.GNU/Linux 시스템 호출을 사용하여 Haskell에서 Socket to Socket 데이터 전송을위한 'splice'호출
또한 (데이터 전송을 위해 잘 알려진 소켓 소켓 OS 특정 휴대용 구현 루프를 가지고)splice
라는 새로운 하스켈 패키지가있다.
나는 다음 (하스켈) 코드를 가지고 :
#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
참고 : 지금 위코드는 단지 작품! 다음은 Nemo에게 더 이상 유효하지 않습니다! (이미 소켓 API send
및 recv
호출을 사용하여 데이터 교환 데이터의 최소량을 전송하는데 사용되거나 핸들로 변환 hGetLine
및 hPut
함께 사용되는) 두 개방형 접속 소켓과 상기 정의 된 바와 같은 I가 splice
전화
및 첫 번째 c_splice
호출 사이트에서
Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)
: 나는 점점 계속 c_splice
반환 -1
와 어 resource exhausted | resource temporarily unavailable
를 읽고 값 (아마 EAGAIN
) 일부 errno
을 설정 엉 위로 보았다.
다른 Length
값을 가진 splice
을 호출하여 테스트했습니다. 1024
, 8192
.
현재 버전에서는 splice()를 호출 할 때마다 새 파이프가 만들어집니다. 대용량 블록을 항상 움직이는 경우에는 큰 문제가 아니지만 작은 블록 일 경우에는 문제가 없습니다. 보통 파이프를 소유하기 위해 "Splicer"개체를 만든 다음 데이터를 이동시키기 위해 +에서 설명자로 반복적으로 호출합니다. – Nemo
@nemo'splice' ('c_splice'가 아니라)는 실제로'영원히 '때문에 무한 루프입니다. 루프 스플 라이스 (loopSplice)와 같은 이름으로 스플 라이스 (splice) 이름을 변경하여 명확하게해야한다고 생각합니다. 그래서 현재는 각'c_splice' 호출마다가 아닌 프록시 연결마다 하나의 파이프를 생성합니다. –
Windows에서 이식 가능한 구현을 테스트하기에는 여전히 많은 부분이 있지만 그래도 더 나은 이름을 생각하는 데 충분한 시간을 할애 할 것입니다. 당신의 제안을 위해서도 열어 둡니다. –