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.2.0
+
+* Clean and document exception behavior.
+
 ## 0.2.1.0
 
 * Fix broken document.
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
@@ -1,5 +1,4 @@
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE DeriveDataTypeable #-}
 
 -- | This module provides convenience functions for interfacing @io-streams@
 -- with @HsOpenSSL@. @ssl/SSL@ here stand for @HsOpenSSL@ library, not the
@@ -7,7 +6,8 @@
 -- sending is unbuffered, anything write into 'OutputStream' will be immediately
 -- send to underlying socket.
 --
--- You should handle 'IOError' when you read/write these streams for safety.
+-- The same exceptions rule which applied to TCP apply here, with addtional
+-- 'SSL.SomeSSLException` to be watched out.
 --
 -- Be sure to use 'withOpenSSL' wrap your operation before using any functions here.
 -- otherwise a segmentation fault will happen.
@@ -22,11 +22,10 @@
   , withOpenSSL
   , sslToStreams
   , closeSSL
-  , CertificateVerifyFail(..)
   ) where
 
 import qualified Control.Exception     as E
-import           Control.Monad         (void, unless)
+import           Control.Monad         (unless, void)
 import           Data.ByteString.Char8 (ByteString)
 import qualified Data.ByteString.Char8 as S
 import           Network.Socket        (HostName, PortNumber, Socket)
@@ -38,7 +37,6 @@
 import           System.IO.Streams     (InputStream, OutputStream)
 import qualified System.IO.Streams     as Streams
 import qualified System.IO.Streams.TCP as TCP
-import           Data.Typeable         (Typeable)
 
 bUFSIZ :: Int
 bUFSIZ = 32752
@@ -57,6 +55,7 @@
     input = do
         s <- SSL.read ssl bUFSIZ
         return $! if S.null s then Nothing else Just s
+        `E.onException` return Nothing
 
     output Nothing  = return ()
     output (Just s) = SSL.write ssl s
@@ -70,8 +69,7 @@
 -- @('HostName', 'PortNumber')@ combination.
 --
 -- this function will try to verify server's identity,
--- a 'CertificateVerifyFail' will be thrown if fail.
--- it may throw 'SSL.SomeSSLException' too.
+-- a 'SSL.ProtocolError' will be thrown if fail.
 --
 connect :: SSLContext           -- ^ SSL context. See the @HsOpenSSL@
                                 -- documentation for information on creating
@@ -92,16 +90,17 @@
             verified = case subname of
                 Just subname' -> maybe False (== subname') cnname
                 Nothing       -> maybe False (matchDomain host) cnname
-        unless (trusted && verified) (E.throwIO CertificateVerifyFail)
+        unless (trusted && verified) (E.throwIO $ SSL.ProtocolError "fail to verify certificate")
         (is, os) <- sslToStreams ssl
         return (is, os, ssl)
 
   where
     matchDomain :: String -> String -> Bool
     matchDomain n1 n2 =
-        let n1' = (take 2 . reverse) (splitDot n1)
-            n2' = (take 2 . reverse) (splitDot n2)
-        in n1' == n2'
+        let n1' = reverse (splitDot n1)
+            n2' = reverse (splitDot n2)
+            cmp src target = src == "*" || src == target
+        in and (zipWith cmp n1' n2')
     splitDot :: String -> [String]
     splitDot "" = [""]
     splitDot x  =
@@ -146,9 +145,7 @@
     E.bracketOnError (SSL.connection ctx sock') closeSSL $ \ ssl -> do
         SSL.accept ssl
         trusted <- SSL.getVerifyResult ssl
-        unless trusted (E.throwIO CertificateVerifyFail)
+        unless trusted (E.throwIO $ SSL.ProtocolError "fail to verify certificate")
         (is, os) <- sslToStreams ssl
         return (is, os, ssl, sockAddr)
 
-data CertificateVerifyFail = CertificateVerifyFail deriving (Show, Eq, Typeable)
-instance E.Exception CertificateVerifyFail
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,8 +4,15 @@
 -- with raw tcp. the default receive buffer size is 4096. sending is unbuffered,
 -- anything write into 'OutputStream' will be immediately send to underlying socket.
 --
--- You should handle 'IOError' when you read/write these streams for safety.
--- Note, `TCP_NODELAY` are enabled by default. you can use 'N.setSocketOption' to adjust.
+-- Reading 'InputStream' will block until message comming, 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'),
+-- 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).
+--
+-- `TCP_NODELAY` are enabled by default. you can use 'N.setSocketOption' to adjust.
 
 module System.IO.Streams.TCP (
     -- * tcp client
@@ -18,22 +25,23 @@
   , accept
   ) where
 
+import           Control.Concurrent.MVar   (withMVar)
 import qualified Control.Exception         as E
-import           Control.Monad             (void)
-import           Data.ByteString.Char8     (ByteString)
-import           Network.Socket            (HostName, PortNumber, Socket)
+import           Control.Monad             (unless, void)
+import           Data.ByteString           (ByteString)
+import qualified Data.ByteString           as B
+import           Network.Socket            (HostName, PortNumber, Socket (..))
 import qualified Network.Socket            as N
+import qualified Network.Socket.ByteString as NB
 import           System.IO.Streams         (InputStream, OutputStream)
 import qualified System.IO.Streams         as Stream
-import           System.IO.Streams.Network (socketToStreamsWithBufferSize)
 
-
 bUFSIZ :: Int
 bUFSIZ = 4096
 
 -- | resolve a 'HostName'/'PortNumber' combination.
 --
--- This function throws an IO exception when resolve fail.
+-- This function throws an 'IOError' when resolve fail.
 --
 resolveAddrInfo :: HostName -> PortNumber -> IO (N.Family, N.SocketType, N.ProtocolNumber, N.SockAddr)
 resolveAddrInfo host port = do
@@ -74,7 +82,6 @@
                      )
 
 
-
 -- | connect to remote tcp server.
 --
 -- You may need to use 'E.bracket' pattern to enusre 'N.Socket' 's safety.
@@ -82,11 +89,7 @@
 connect :: HostName             -- ^ hostname to connect to
         -> PortNumber           -- ^ port number to connect to
         -> IO (InputStream ByteString, OutputStream ByteString, Socket)
-connect host port = do
-    sock <- connectSocket host port
-    (is, os) <- socketToStreamsWithBufferSize bUFSIZ sock
-    return (is, os, sock)
-
+connect host port = connectWithBufferSize host port bUFSIZ
 
 -- | connect to remote tcp server with a receive buffer size.
 --
@@ -143,10 +146,31 @@
 -- | 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 current thread if there's no connection comming.
+-- This function will block if there's no connection comming.
 --
 accept :: Socket -> IO (InputStream ByteString, OutputStream ByteString, N.Socket, N.SockAddr)
 accept sock = do
     (sock', sockAddr) <- N.accept sock
     (is, os) <- socketToStreamsWithBufferSize bUFSIZ sock'
     return (is, os, sock', sockAddr)
+
+socketToStreamsWithBufferSize
+    :: Int                      -- ^ how large the receive buffer should be
+    -> Socket                   -- ^ network socket
+    -> IO (InputStream ByteString, OutputStream ByteString)
+socketToStreamsWithBufferSize bufsiz sock@(MkSocket _ _ _ _ statusMVar) = do
+    is <- Stream.makeInputStream input
+    os <- Stream.makeOutputStream output
+    return (is, os)
+
+  where
+    input = withMVar statusMVar $ \ status ->
+        case status of
+            N.Connected -> do
+                s <- NB.recv sock bufsiz
+                return $! if B.null s then Nothing else Just s
+                `E.onException` return Nothing
+            _ -> return Nothing
+
+    output Nothing  = return ()
+    output (Just s) = unless (B.null s) (NB.sendAll sock s)
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
@@ -5,7 +5,8 @@
 -- sending is unbuffered, anything write into 'OutputStream' will be
 -- immediately send to underlying socket.
 --
--- You should handle 'IOError' when you read/write these streams for safety.
+-- The same exceptions rule which applied to TCP apply here, with addtional
+-- 'TLS.TLSException' to be watched out.
 --
 module System.IO.Streams.TLS (
     -- * client
@@ -46,6 +47,7 @@
     input = do
         s <- TLS.recvData ctx
         return $! if B.null s then Nothing else Just s
+        `E.onException` return Nothing
 
     output Nothing  = return ()
     output (Just s) = TLS.sendData ctx (fromStrict s)
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.1.0
+version:             0.2.2.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
