7

更新:ネモ氏の答えは問題の解決に役立ちました!以下のコードには修正が含まれています!nb False以下のおよびnb True呼び出しを参照してください。

splice(OS固有で最もよく知られているソケット間データ転送ループのポータブル実装を備えた)と呼ばれる新しいHaskellパッケージもあります

私は次の(Haskell)コードを持っています:

#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のおかげで、以下は無効になりました。

上記で定義したように、2つの開いた接続されたソケット(ソケットAPIと呼び出しspliceのいずれかを使用して最小限のハンドシェイクデータを送信するためにすでに使用されているか、ハンドルに変換されてとで使用されます)を使用して呼び出し、次のようになります。sendrecvhGetLinehPut

Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)

最初のc_splice呼び出しサイトで:検索時に読み取る値(おそらく)にいくつかをc_splice返し-1、設定します。errnoEAGAINresource exhausted | resource temporarily unavailable

spliceさまざまな値で呼び出しをテストしましたLength10248192

4

2 に答える 2

13

Haskellはわかりませんが、「リソースが一時的に利用できません」ですEAGAIN

そして、Haskellはデフォルトでソケットを非ブロッキングモードに設定しているようです。したがって、データがないときに1つから読み取ろうとしたり、バッファがいっぱいになったときに1つに書き込もうとすると、で失敗しEAGAINます。

ソケットをブロッキングモードに変更する方法を理解してください。そうすれば、問題は解決するはずです。

[アップデート]

または、ソケットの読み取りまたは書き込みを試みる前に、selectまたはを呼び出します。ただし、実際にはソケットの準備ができていないのにLinuxがソケットの準備ができていることを示すというまれなコーナーケースがあるため、poll処理する必要があります。EAGAINselect

于 2012-04-09T23:13:14.750 に答える
0

システムコールはあなたのsendfile()ために働きますか?その場合は、sendfileパッケージを使用できます。

于 2012-04-09T23:27:47.673 に答える