本文介绍了使用GNU / Linux系统调用`splice`在Haskell中进行Zero-Copy Socket到Socket的数据传输的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:Nemo先生的回答有助于解决问题!下面的代码包含修复!请参阅下面的 nb False 和 nb True 调用。



还有一个新的Haskell软件包叫做 (),它具有OS最具知名的套接字至套接字数据传输循环的特定于操作系统的可移植实现。

我有以下代码(Haskell):

  #ifdef LINUX_SPLICE 
#include< fcntl.h>
{ - #LANGUAGE CPP# - }
{ - #LANGUAGE ForeignFunctionInterface# - }
#endif

模块Network.Socket.Splice(
长度
,zeroCopy
,splice
#ifdef LINUX_SPLICE
,c_splice
#endif
)其中

import Data.Word
导入Foreign.Ptr

导入Network.Socket
导入Control.Monad
导入Control.Exception
导入System.Posix.Types
导入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


类型长度=
#ifdef LINUX_SPLICE
(#type size_t)
#else
Int
#endif


- | 'splice'函数将数据从
- 一个套接字传递到另一个套接字。
- 在Linux上,这发生在内核空间中,
- 在内核和用户空间之间零复制。
- 在其他操作系统上,便携式
- 实现使用用户空间缓冲区
- 用'mallocBytes'分配;然后使用'hGetBufSome'
和'hPut'来避免重复的
- 与'recv'
- 'bytestring'包中的'sendAll'调用发生的微小分配。
splice :: Length - >套接字 - >套接字 - > IO()
splice l(MkSocket x _ _ _ _)(MkSocket y _ _ _ _)= do

let e = error拼接结束

#ifdef LINUX_SPLICE

(r,w)< - createPipe
print('+',r,w)
let s = Fd x - source
let t = Fd y - target
let c = throwErrnoIfMinus1Network.Socket.Splice.splice
let u = unsafeCoerce ::(#type ssize_t) - > (#type size_t)
让fs = sPLICE_F_MOVE。|。 sPLICE_F_MORE
let nb v = do setNonBlockingFD xv
setNonBlockingFD yv
nb False
finally
(forever $ do
b< - c $ c_splice s nullPtr w nullPtr l fs
如果b> 0
则c_splice r nullPtr t nullPtr(ub)fs)
else e
(do closeFd r
closeFd w
nb True
print(' - ',r,w))

#else

- ..

#endif


#ifdef LINUX_SPLICE
- SPLICE

- fcntl.h
- ssize_t拼接(
- int fd_in,
- loff_t * off_in,
- int fd_out,
- loff_t * off_out,
- size_t len,
- unsigned int flags
- );

国外进口ccall拼接
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 =(#constSPLICE_F_MOVE)

sPLICE_F_MORE :: Word
sPLICE_F_MORE =( #constSPLICE_F_MORE)
#endif

注意: 现在代码正常工作!以下内容不再有效,感谢Nemo! 我打电话给 splice ,如上面定义的那样,使用两个开放和连接的套接字(它们已经被用于使用套接字API send 和 recv 调用或转换为句柄,并与 hGetLine 和 hPut
$ p $ Network.Socket.Splice.splice:资源耗尽(资源暂时不可用)

在第一个 c_splice 呼叫站点: c_splice 返回 -1 并将一些 errno 设置为一个值(可能 EAGAIN )读取资源耗尽|资源暂时不可用查找时。



我测试了 splice c $ c>长度值: 1024 , 8192 。

解决方案

我不知道Haskell,但资源暂时不可用是 EAGAIN p>

它看起来像。因此,如果您在没有数据时尝试从一个数据读取数据,或者在其缓冲区已满时尝试写入一个数据,则您将失败,并且 EAGAIN 。



找出如何将套接字更改为阻塞模式,并且我敢打赌,您将解决您的问题。

[update]



或者,在尝试读取或调用 c> select 或 poll 编写套接字。但是您仍然需要处理 EAGAIN ,因为在Linux select 时会出现少数情况,表明套接字已准备好实际上它不是。


Update: Mr. Nemo's answer helped solve the problem! The code below contains the fix! See the nb False and nb True calls below.

There is also a new Haskell package called splice (, which has OS-specific and portable implementations of best known socket to socket data transfer loops).

I have the following (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

Note: The code above now just works! Below is no longer valid thanks to Nemo!

I call splice as defined above with two open and connected sockets (which are already used to transmit minimal amount of handshake data using either the sockets API send and recv calls or converted to handles and used with hGetLine and hPut) and I keep getting:

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

at the first c_splice call site: c_splice returns -1 and sets some errno to a value (probably EAGAIN) that reads resource exhausted | resource temporarily unavailable when looked up.

I tested calling splice with different Length values: 1024, 8192.

解决方案

I don't know Haskell, but "resource temporarily unavailable" is EAGAIN.

And it looks like Haskell sets its sockets to non-blocking mode by default. So if you try to read from one when there is no data, or try to write to one when its buffer is full, you will fail with EAGAIN.

Figure out how to change the sockets to blocking mode, and I bet you will solve your problem.

[update]

Alternatively, call select or poll before attempting to read or write the socket. But you still need to handle EAGAIN, because there are rare corner cases where Linux select will indicate a socket is ready when actually it isn't.

这篇关于使用GNU / Linux系统调用`splice`在Haskell中进行Zero-Copy Socket到Socket的数据传输的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-31 10:47