अद्यतन: श्री निमो के उत्तर ने समस्या को हल करने में मदद की! नीचे दिए गए कोड में फिक्स शामिल है! नीचे nb False
और nb True
कॉल देखें।जीएसयू/लिनक्स सिस्टम का उपयोग करना शून्य-प्रतिलिपि सॉकेट के लिए 'स्प्लिस' को सॉस्क डेटा ट्रांसफर में सॉस्क डेटा ट्रांसफर के लिए कॉल करें
वहाँ भी एक नया हास्केल पैकेज 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
नोट:कोड अब ऊपर सिर्फ काम करता है! नीचे निमो के लिए वैध धन्यवाद नहीं है!
मैं splice
फोन के रूप में दो खुला और जुड़े सॉकेट (जो पहले से ही या तो सॉकेट एपीआई send
और recv
कॉल का उपयोग हाथ मिलाना डेटा की न्यूनतम राशि हस्तांतरित करने के लिए प्रयोग किया जाता है या हैंडल करने के लिए परिवर्तित और hGetLine
और hPut
साथ किया जाता है) के साथ ऊपर परिभाषित और मैं बार आ रही है:
Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)
पहले c_splice
कॉल स्थल पर: c_splice
रिटर्न -1
और एक मूल्य (शायद EAGAIN
) कि resource exhausted | resource temporarily unavailable
क पढ़ता करने के लिए कुछ errno
सेट एन देखा।
मैंने विभिन्न Length
मूल्यों के साथ splice
पर कॉल करने का परीक्षण किया: 1024
, 8192
।
हर बार जब आप splice() को कॉल करते हैं तो आपका वर्तमान संस्करण एक नई पाइप बनाता है। यह ठीक है अगर आप हमेशा बड़े ब्लॉक ले जा रहे हैं, लेकिन छोटे ब्लॉक के लिए जो एक बड़े ओवरहेड लगा सकते हैं। मैं आम तौर पर पाइप के मालिक होने के लिए "स्प्लिसर" ऑब्जेक्ट बनाता हूं, फिर डेटा को स्थानांतरित करने के लिए इसे बार-बार वर्णित करने के लिए कॉल करता हूं। – Nemo
@ नीमो 'splice' (' c_splice' नहीं) वास्तव में 'हमेशा के लिए' एक अनंत लूप है। मुझे लगता है कि मुझे स्पष्ट करने के लिए 'loopSplice' जैसे कुछ को 'splice' का नाम बदलना चाहिए। तो वर्तमान में यह प्रत्येक पाइप प्रति प्रॉक्सी कनेक्शन बनाता है जो प्रत्येक 'c_splice' कॉल के अनुसार नहीं होता है। –
मेरे पास अभी भी विंडोज़ पर पोर्टेबल कार्यान्वयन के साथ परीक्षण करने के लिए बहुत कुछ है, हालांकि मेरे पास निश्चित रूप से बेहतर नाम पर विचार करने के लिए पर्याप्त समय होगा। अपने सुझावों के लिए भी खोलें :) –