diff --git a/network-unexceptional.cabal b/network-unexceptional.cabal
--- a/network-unexceptional.cabal
+++ b/network-unexceptional.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: network-unexceptional
-version: 0.1.1.0
+version: 0.1.2.0
 synopsis: Network functions that do not throw exceptions
 description:
   Functions compatible with the Socket type from the network library that
@@ -19,13 +19,14 @@
   ghc-options: -O2 -Wall
   exposed-modules:
     Network.Unexceptional
-    Network.Unexceptional.Bytes
+    Network.Unexceptional.ByteArray
     Network.Unexceptional.ByteString
+    Network.Unexceptional.Bytes
     Network.Unexceptional.Chunks
     Network.Unexceptional.MutableBytes
     Network.Unexceptional.Types
   build-depends:
-    , base >=4.17 && <5
+    , base >=4.16.3 && <5
     , posix-api >=0.6.1
     , error-codes >=0.1.1
     , byteslice >=0.2.8
diff --git a/src/Network/Unexceptional/ByteArray.hs b/src/Network/Unexceptional/ByteArray.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Unexceptional/ByteArray.hs
@@ -0,0 +1,48 @@
+{-# language BangPatterns #-}
+{-# language DuplicateRecordFields #-}
+{-# language PatternSynonyms #-}
+{-# language LambdaCase #-}
+{-# language NamedFieldPuns #-}
+
+module Network.Unexceptional.ByteArray
+  ( receiveExactly
+  ) where
+
+import Control.Exception (throwIO)
+import Control.Monad (when)
+import Data.Array.Byte (ByteArray)
+import Data.Bytes.Types (Bytes(Bytes),MutableBytes(MutableBytes))
+import Foreign.C.Error (Errno)
+import Foreign.C.Error.Pattern (pattern EWOULDBLOCK,pattern EAGAIN)
+import GHC.Conc (threadWaitWrite)
+import Network.Socket (Socket)
+import System.Posix.Types (Fd(Fd))
+
+import qualified Network.Unexceptional.Types as Types
+import qualified Posix.Socket as X
+import qualified Linux.Socket as X
+import qualified Data.Bytes.Types
+import qualified Network.Socket as S
+import qualified Data.Primitive as PM
+import qualified Network.Unexceptional.MutableBytes as MB
+
+-- | Blocks until an exact number of bytes has been received.
+receiveExactly :: 
+     Socket
+  -> Int -- ^ Exact number of bytes to receive, must be greater than zero
+  -> IO (Either Errno ByteArray)
+receiveExactly s n = if n > 0
+  then do
+    dst <- PM.newByteArray n
+    let loop !ix !remaining = case remaining of
+          0 -> do
+            dst' <- PM.unsafeFreezeByteArray dst 
+            pure (Right dst')
+          _ -> MB.receive s (MutableBytes dst ix remaining) >>= \case
+            Left e -> pure (Left e)
+            Right k -> loop (ix + k) (remaining - k)
+    loop 0 n
+  else throwIO Types.NonpositiveReceptionSize
+
+
+
