2012-04-09 8 views
7

अद्यतन: श्री निमो के उत्तर ने समस्या को हल करने में मदद की! नीचे दिए गए कोड में फिक्स शामिल है! नीचे 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

+1

हर बार जब आप splice() को कॉल करते हैं तो आपका वर्तमान संस्करण एक नई पाइप बनाता है। यह ठीक है अगर आप हमेशा बड़े ब्लॉक ले जा रहे हैं, लेकिन छोटे ब्लॉक के लिए जो एक बड़े ओवरहेड लगा सकते हैं। मैं आम तौर पर पाइप के मालिक होने के लिए "स्प्लिसर" ऑब्जेक्ट बनाता हूं, फिर डेटा को स्थानांतरित करने के लिए इसे बार-बार वर्णित करने के लिए कॉल करता हूं। – Nemo

+0

@ नीमो 'splice' (' c_splice' नहीं) वास्तव में 'हमेशा के लिए' एक अनंत लूप है। मुझे लगता है कि मुझे स्पष्ट करने के लिए 'loopSplice' जैसे कुछ को 'splice' का नाम बदलना चाहिए। तो वर्तमान में यह प्रत्येक पाइप प्रति प्रॉक्सी कनेक्शन बनाता है जो प्रत्येक 'c_splice' कॉल के अनुसार नहीं होता है। –

+0

मेरे पास अभी भी विंडोज़ पर पोर्टेबल कार्यान्वयन के साथ परीक्षण करने के लिए बहुत कुछ है, हालांकि मेरे पास निश्चित रूप से बेहतर नाम पर विचार करने के लिए पर्याप्त समय होगा। अपने सुझावों के लिए भी खोलें :) –

उत्तर

12

मुझे हास्केल नहीं पता, लेकिन "संसाधन अस्थायी रूप से अनुपलब्ध" EAGAIN है।

और यह डिफ़ॉल्ट रूप से Haskell sets its sockets to non-blocking mode जैसा दिखता है। इसलिए यदि कोई डेटा नहीं होने पर आप एक से पढ़ने की कोशिश करते हैं, या उसके बफर भरने पर एक को लिखने का प्रयास करते हैं, तो आप EAGAIN से असफल हो जाएंगे।

सॉकेट को अवरुद्ध करने के तरीके को बदलने के तरीके को चित्रित करें, और मुझे लगता है कि आप अपनी समस्या का समाधान करेंगे।

[अद्यतन]

वैकल्पिक रूप से, पढ़ने के लिए या सॉकेट लिखने के लिए प्रयास करने से पहले फोन select या poll। लेकिन आपको अभी भी EAGAIN को संभालने की आवश्यकता है, क्योंकि दुर्लभ कोने के मामले हैं जहां लिनक्स select इंगित करेगा कि जब सॉकेट वास्तव में नहीं होता है तो सॉकेट तैयार होता है।

+0

संकेत के लिए धन्यवाद! मैं यह देखने के लिए देख रहा हूं कि यह कितनी दूर मदद करता है :) –

+2

वाह आप हास्केल को नहीं जानते हैं फिर भी आप मेरी समस्या, अद्भुत उत्तर को हल करने में मदद करने के लिए सटीक रेखा को इंगित कर सकते हैं !! –

+0

यह समस्या को सुलझाने वाले 'स्प्लिसे' कॉल के आस-पास गैर-अवरुद्ध मोड को उचित रूप से सेट कर रहा था। मैं आपके उत्तम दर्जे का जवाब के लिए वास्तव में आभारी हूं। :) –

0

sendfile() syscall आपके लिए काम करेगा? यदि ऐसा है तो आप sendfile package का उपयोग कर सकते हैं।

+1

धन्यवाद। इसके अनुसार: http://kerneltrap.org/node/6505 'splice' दोनों पक्षों को सॉकेट होने का सही तरीका है। क्या मै गलत हु? बीटीडब्ल्यू निमो के जवाब ने मेरे लिए समस्या हल की है, इसलिए मैं 'splice' के कार्यान्वयन के साथ रहूंगा! –

+0

मेरे कोड में टिप्पणी किए गए अनुभाग में 'मॉलोकबाइट्स', 'एचजीएटी बुफसोम' और 'एचपीयूटी' का उपयोग करके उपयोगकर्ता स्पेस कोड का एक पोर्टेबल टुकड़ा होता है जो 'नेटवर्क-बाइटस्ट्रिंग' के 'रिकव', 'sendAll'' से बेहतर प्रदर्शन करता है। मैं इसे पॉलिश कर दूंगा और इसे जल्द ही हैकेज पर रखूंगा। मैं एक उच्च प्रदर्शन, बेहद सरल, छोटे और स्वच्छ प्रॉक्सी एप्लिकेशन पर काम कर रहा हूं और इसके डिजाइन सिद्धांतों में से एक यह है: ** बाहरी पैकेजों पर पूरी तरह से आवश्यक नहीं है जब तक कि पूरी तरह से आवश्यक नहीं है ** और हालांकि 'sendfile' की निर्भरता सूची भी रास्ता है अधिकांश अन्य पैकेजों की तुलना में साफ, मैंने प्रतिद्वंद्वी प्रॉक्सी सॉफ़्टवेयर को 'splice' का उपयोग करके भी देखा है। :) –

+1

'sendfile' एक फ़ाइल भेजता है। यह काम नहीं करता है अगर स्रोत फ़ाइल नहीं है या गंतव्य गंतव्य सॉकेट नहीं है। 'splice' वह है जिसे आपको फ़ाइल-टू-फाइल या सॉकेट-टू-सॉकेट शून्य-प्रतिलिपि के लिए आवश्यक है। (हालांकि यह आमतौर पर वास्तव में शून्य प्रतिलिपि नहीं है ... लंबी कहानी) – Nemo

संबंधित मुद्दे