diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,9 @@
 # Revision history for tcp-simple
 
+## 0.2.3.0
+
+* Add `acceptWithBufferSize`, `socketToStreamsWithBufferSize`, fix recv exception handler.
+
 ## 0.2.2.0
 
 * Clean and document exception behavior.
diff --git a/System/IO/Streams/OpenSSL.hs b/System/IO/Streams/OpenSSL.hs
--- a/System/IO/Streams/OpenSSL.hs
+++ b/System/IO/Streams/OpenSSL.hs
@@ -52,13 +52,14 @@
     return (is, os)
 
   where
-    input = do
+    input = ( do
         s <- SSL.read ssl bUFSIZ
         return $! if S.null s then Nothing else Just s
-        `E.onException` return Nothing
+        ) `E.catch` (\(_::E.SomeException) -> return Nothing)
 
     output Nothing  = return ()
     output (Just s) = SSL.write ssl s
+{-# INLINABLE sslToStreams #-}
 
 closeSSL :: SSL.SSL -> IO ()
 closeSSL ssl = do
@@ -132,7 +133,7 @@
     eatException m = void m `E.catch` (\(_::E.SomeException) -> return ())
 
 
--- | accept a new connection from remote client, return a 'InputStream' / 'OutputStream'
+-- | Accept a new connection from remote client, return a 'InputStream' / 'OutputStream'
 -- pair and remote 'N.SockAddr', you should call 'TCP.bindAndListen' first.
 --
 -- this operation will throw 'SSL.SomeSSLException' on failure.
diff --git a/System/IO/Streams/TCP.hs b/System/IO/Streams/TCP.hs
--- a/System/IO/Streams/TCP.hs
+++ b/System/IO/Streams/TCP.hs
@@ -4,10 +4,10 @@
 -- with raw tcp. the default receive buffer size is 4096. sending is unbuffered,
 -- anything write into 'OutputStream' will be immediately send to underlying socket.
 --
--- Reading 'InputStream' will block until message comming, for example
--- `System.IO.Streams.ByteString.readExactly 1024` will block until 1024 bytes are available.
+-- Reading 'InputStream' will block until GHC IO manager find data is ready,
+-- for example 'System.IO.Streams.ByteString.readExactly 1024' will block until 1024 bytes are available.
 --
--- When socket is closed, the 'InputStream' will be closed too(further reading 'Nothing'),
+-- When socket is closed, the 'InputStream' will be closed too(further reading will return 'Nothing'),
 -- no exception will be thrown. You still should handle 'IOError' when you write to  'OutputStream' for safety,
 -- but no exception doesn't essentially mean a successful write, especially under bad network
 -- environment(broken wire for example).
@@ -23,6 +23,10 @@
     -- * tcp server
   , bindAndListen
   , accept
+  , acceptWithBufferSize
+    -- * helpers
+  , socketToStreamsWithBufferSize
+  , N.close
   ) where
 
 import           Control.Concurrent.MVar   (withMVar)
@@ -39,7 +43,7 @@
 bUFSIZ :: Int
 bUFSIZ = 4096
 
--- | resolve a 'HostName'/'PortNumber' combination.
+-- | Resolve a 'HostName'/'PortNumber' combination.
 --
 -- This function throws an 'IOError' when resolve fail.
 --
@@ -82,7 +86,7 @@
                      )
 
 
--- | connect to remote tcp server.
+-- | Connect to remote tcp server.
 --
 -- You may need to use 'E.bracket' pattern to enusre 'N.Socket' 's safety.
 --
@@ -91,7 +95,7 @@
         -> IO (InputStream ByteString, OutputStream ByteString, Socket)
 connect host port = connectWithBufferSize host port bUFSIZ
 
--- | connect to remote tcp server with a receive buffer size.
+-- | Connect to remote tcp server with adjustable receive buffer size.
 --
 connectWithBufferSize :: HostName             -- ^ hostname to connect to
                       -> PortNumber           -- ^ port number to connect to
@@ -119,13 +123,12 @@
   where
     go (is, os, sock) = action is os sock
 
-    cleanup (_, os, sock) = E.mask_ $ do
-        eatException $! Stream.write Nothing os
-        eatException $! N.close sock
+    cleanup (_, os, sock) = E.mask_ $
+        eatException $! Stream.write Nothing os >> N.close sock
 
     eatException m = void m `E.catch` (\(_::E.SomeException) -> return ())
 
--- | bind and listen on port with a limit on connection count.
+-- | Bind and listen on port with a limit on connection count.
 --
 bindAndListen :: PortNumber -> Int -> IO Socket
 bindAndListen port maxc = do
@@ -143,17 +146,32 @@
                                   return sock
                      )
 
--- | accept a new connection from remote client, return a 'InputStream' / 'OutputStream' pair,
+-- | Accept a new connection from remote client, return a 'InputStream' / 'OutputStream' pair,
 -- a new underlying 'Socket', and remote 'N.SockAddr',you should call 'bindAndListen' first.
 --
 -- This function will block if there's no connection comming.
 --
 accept :: Socket -> IO (InputStream ByteString, OutputStream ByteString, N.Socket, N.SockAddr)
-accept sock = do
+accept sock = acceptWithBufferSize sock bUFSIZ
+
+
+-- | accept a connection with adjustable receive buffer size.
+--
+acceptWithBufferSize :: Socket -> Int -> IO (InputStream ByteString, OutputStream ByteString, N.Socket, N.SockAddr)
+acceptWithBufferSize sock bufsiz = do
     (sock', sockAddr) <- N.accept sock
-    (is, os) <- socketToStreamsWithBufferSize bUFSIZ sock'
+    (is, os) <- socketToStreamsWithBufferSize bufsiz sock'
     return (is, os, sock', sockAddr)
 
+
+-- | Convert a 'Socket' into a streams pair, catch 'IOException's on receiving and close 'InputStream'.
+-- You still should handle 'IOError' when you write to  'OutputStream' for safety,
+-- but no exception doesn't essentially mean a successful write, especially under bad network
+-- environment(broken wire for example).
+--
+-- During receiving, the status 'Control.Concurrent.MVar.MVar' is locked, so that a cleanup thread
+-- can't affect receiving until finish.
+--
 socketToStreamsWithBufferSize
     :: Int                      -- ^ how large the receive buffer should be
     -> Socket                   -- ^ network socket
@@ -162,15 +180,15 @@
     is <- Stream.makeInputStream input
     os <- Stream.makeOutputStream output
     return (is, os)
-
   where
     input = withMVar statusMVar $ \ status ->
         case status of
-            N.Connected -> do
+            N.Connected -> ( do
                 s <- NB.recv sock bufsiz
                 return $! if B.null s then Nothing else Just s
-                `E.onException` return Nothing
+                ) `E.catch` (\(_::E.IOException) -> return Nothing)
             _ -> return Nothing
 
     output Nothing  = return ()
     output (Just s) = unless (B.null s) (NB.sendAll sock s)
+{-# INLINABLE socketToStreamsWithBufferSize #-}
diff --git a/System/IO/Streams/TLS.hs b/System/IO/Streams/TLS.hs
--- a/System/IO/Streams/TLS.hs
+++ b/System/IO/Streams/TLS.hs
@@ -44,16 +44,16 @@
     os <- Stream.makeOutputStream output
     return (is, os)
   where
-    input = do
+    input = ( do
         s <- TLS.recvData ctx
         return $! if B.null s then Nothing else Just s
-        `E.onException` return Nothing
+        ) `E.catch` (\(_::E.SomeException) -> return Nothing)
 
     output Nothing  = return ()
     output (Just s) = TLS.sendData ctx (fromStrict s)
-
+{-# INLINABLE tlsToStreams #-}
 
--- | close a TLS 'Context' and its underlying socket.
+-- | Close a TLS 'Context' and its underlying socket.
 --
 closeTLS :: Context -> IO ()
 closeTLS ctx = TLS.bye ctx >> TLS.contextClose ctx
@@ -105,7 +105,7 @@
     eatException m = void m `E.catch` (\(_::E.SomeException) -> return ())
 
 
--- | accept a new connection from remote client, return a 'InputStream' / 'OutputStream'
+-- | Accept a new connection from remote client, return a 'InputStream' / 'OutputStream'
 -- pair and remote 'N.SockAddr', you should call 'TCP.bindAndListen' first.
 --
 -- this operation will throw 'TLS.TLSException' on failure.
diff --git a/tcp-streams.cabal b/tcp-streams.cabal
--- a/tcp-streams.cabal
+++ b/tcp-streams.cabal
@@ -1,5 +1,5 @@
 name:                tcp-streams
-version:             0.2.2.0
+version:             0.2.3.0
 synopsis:            One stop solution for tcp client and server with tls support.
 description:         One stop solution for tcp client and server with tls support.
 license:             BSD3
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -61,9 +61,8 @@
         sock <- Raw.bindAndListen 8888 1024
         putMVar mvar ()
         (is, os, csock, _) <- Raw.accept sock
-        Stream.toList is >>= flip Stream.writeList os
-        os `Stream.connectTo` is
-        N.sClose csock
+        is' <- Stream.atEndOfInput (N.close csock) is
+        os `Stream.connectTo` is'
         N.sClose sock
 
 ------------------------------------------------------------------------------
@@ -98,9 +97,10 @@
         sp <- TLS.makeServerParams "./test/cert/server.crt" [] "./test/cert/server.key"
         sock <- Raw.bindAndListen 8889 1024
         putMVar mvar ()
-        (is, os, _, _) <- TLS.accept sp sock
-        os `Stream.connectTo` is
-
+        (is, os, tls, _) <- TLS.accept sp sock
+        is' <- Stream.atEndOfInput (Stream.write Nothing os >> TLS.closeTLS tls) is
+        os `Stream.connectTo` is'
+        N.sClose sock
 
 testHTTPS :: Test
 testHTTPS = testCase "network/socket" $
@@ -150,8 +150,10 @@
         sp <- SSL.makeServerSSLContext "./test/cert/server.crt" [] "./test/cert/server.key"
         sock <- Raw.bindAndListen 8890 1024
         putMVar mvar ()
-        (is, os, _, _) <- SSL.accept sp sock
-        os `Stream.connectTo` is
+        (is, os, ssl, _) <- SSL.accept sp sock
+        is' <- Stream.atEndOfInput (Stream.write Nothing os >> SSL.closeSSL ssl) is
+        os `Stream.connectTo` is'
+        N.close sock
 
 testHTTPS' :: Test
 testHTTPS' = testCase "network/socket" $
