更新:Nemo先生的回答有助于解决问题!下面的代码包含修复程序!请参阅下面的nb False和nb True调用.
还有一个名为splice
的新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 ('-',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,以下不再有效!
我用上面定义的splice调用了两个打开并连接的套接字(它们已经用于使用套接字API发送和接收调用传输最少量的握手数据或转换为句柄并与hGetLine和hPut一起使用)并且我一直得到:
Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable)
在第一个c_splice调用站点:c_splice返回-1并将一些errno设置为一个读取资源耗尽的值(可能是EAGAIN)查找时资源暂时不可用.
我测试了不同长度值的调用splice:1024,8192.
解决方法
我不知道Haskell,但“资源暂时不可用”是EAGAIN.
它默认看起来像Haskell sets its sockets to non-blocking mode.因此,如果您在没有数据时尝试从一个读取数据,或者在其缓冲区已满时尝试写入一个数据,那么您将失败并使用EAGAIN.
弄清楚如何将套接字更改为阻止模式,我打赌你会解决你的问题.
[更新]
或者,在尝试读取或写入套接字之前调用select或poll.但是你仍然需要处理EAGAIN,因为有一些罕见的极端情况,其中Linux选择将指示套接字已准备就绪,而实际上并非如此.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。