diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,15 @@
+# Version 0.4.3
+
+* Added SOCKS5 proxy support using the `socks` library. See functions
+  `connectSOCKS5` and `connectSockSOCKS5`.
+
+* Increased connection timeout.
+
+* Client side sockets now have `SO_KEEPALIVE` and `TCP_NODELAY` on by default.
+
+* Server side sockets now have `SO_KEEPALIVE` on by default.
+
+
 # Version 0.4.2
 
 * Deprecate `sendMany` in favor of `sendLazy`.
diff --git a/network-simple.cabal b/network-simple.cabal
--- a/network-simple.cabal
+++ b/network-simple.cabal
@@ -1,5 +1,5 @@
 name:                network-simple
-version:             0.4.2
+version:             0.4.3
 homepage:            https://github.com/k0001/network-simple
 bug-reports:         https://github.com/k0001/network-simple/issues
 license:             BSD3
@@ -35,7 +35,7 @@
   other-modules:     Network.Simple.Internal
   build-depends:     base         (>=4.7 && < 5)
                    , network      (>=2.3)
-                   , bytestring   (>=0.9.2.1)
-                   , transformers (>=0.2)
-                   , exceptions   (>=0.6)
+                   , bytestring
+                   , transformers
                    , safe-exceptions
+                   , socks
diff --git a/src/Network/Simple/Internal.hs b/src/Network/Simple/Internal.hs
--- a/src/Network/Simple/Internal.hs
+++ b/src/Network/Simple/Internal.hs
@@ -13,13 +13,16 @@
   , isIPv6addr
   , prioritize
   , happyEyeballSort
+  , getServicePortNumber'
   ) where
 
-import           Data.Bits                     (shiftR, (.&.))
-import qualified Data.List                     as List
-import           Data.String                   (IsString (fromString))
-import           Data.Word                     (byteSwap32)
-import qualified Network.Socket as             NS
+import Data.Bits (shiftR, (.&.))
+import qualified Data.List as List
+import Data.String (IsString (fromString))
+import Data.Word (byteSwap16)
+import qualified Network.Socket as NS
+import qualified Network.BSD as NS (getServicePortNumber)
+import Text.Read (readMaybe)
 
 -- | Preferred host to bind.
 data HostPreference
@@ -75,3 +78,13 @@
 -- Sorting is stable.
 prioritize :: (a -> Bool) -> [a] -> [a]
 prioritize p = uncurry (++) . List.partition p
+
+-- | Like 'NS.getServicePortNumber', but it will accept numeric values such as
+-- @\"80\"@ as input.
+getServicePortNumber' :: NS.ServiceName -> IO NS.PortNumber
+getServicePortNumber' sn = case readMaybe sn of
+  Just w16 -> pure (fromIntegral (byteSwap16 w16))
+  Nothing -> NS.getServicePortNumber sn
+    -- the @socks5@ library is broken and uses port numbers in the
+    -- wrong byte order. here we work around that.
+
diff --git a/src/Network/Simple/TCP.hs b/src/Network/Simple/TCP.hs
--- a/src/Network/Simple/TCP.hs
+++ b/src/Network/Simple/TCP.hs
@@ -17,6 +17,10 @@
 -- Some code in this file was adapted from the @network-conduit@ library by
 -- Michael Snoyman. Copyright (c) 2011. See its licensing terms (BSD3) at:
 --   https://github.com/snoyberg/conduit/blob/master/network-conduit/LICENSE
+--
+-- Some code in this file was adapted from the @socks@ library by
+-- Vincent Hanquez. Copyright (c) 2010-2011. See its licensing terms (BSD3) at:
+--   https://github.com/vincenthz/hs-socks/blob/master/LICENSE
 
 module Network.Simple.TCP (
   -- * Introduction to TCP networking
@@ -25,6 +29,7 @@
   -- * Client side
   -- $client-side
     connect
+  , connectSOCKS5
 
   -- * Server side
   -- $server-side
@@ -45,6 +50,7 @@
   , bindSock
   , listenSock
   , connectSock
+  , connectSockSOCKS5
   , closeSock
 
   -- * Note to Windows users
@@ -65,9 +71,13 @@
 import           Control.Monad
 import           Control.Monad.IO.Class         (MonadIO(liftIO))
 import qualified Data.ByteString                as BS
+import qualified Data.ByteString.Char8          as B8
 import qualified Data.ByteString.Lazy           as BSL
 import           Data.List                      (transpose, partition)
+import           Data.Word                      (byteSwap16)
 import qualified Network.Socket                 as NS
+import qualified Network.Socks5.Lowlevel        as NS5
+import qualified Network.Socks5.Types           as NS5
 import qualified Network.Socket.ByteString      as NSB
 import qualified Network.Socket.ByteString.Lazy as NSBL
 import qualified System.IO                      as IO
@@ -75,7 +85,7 @@
 
 import Network.Simple.Internal
   (HostPreference(..), hpHostName, isIPv4addr, isIPv6addr, ipv4mapped_to_ipv4,
-   prioritize, happyEyeballSort)
+   prioritize, happyEyeballSort, getServicePortNumber')
 
 --------------------------------------------------------------------------------
 -- $tcp-101
@@ -128,6 +138,8 @@
 --
 -- If you prefer to acquire and close the socket yourself, then use
 -- 'connectSock' and 'closeSock'.
+--
+-- Note: The 'NS.NoDelay' and 'NS.KeepAlive' options are set on the socket.
 connect
   :: (MonadIO m, Ex.MonadMask m)
   => NS.HostName -- ^ Server hostname or IP address.
@@ -137,6 +149,29 @@
   -> m r
 connect host port = Ex.bracket (connectSock host port) (closeSock . fst)
 
+-- | Like 'connect', but connects to the destination server through a SOCKS5
+-- proxy.
+connectSOCKS5
+  :: (MonadIO m, Ex.MonadMask m)
+  => NS.HostName -- ^ SOCKS5 proxy server hostname or IP address.
+  -> NS.ServiceName -- ^ SOCKS5 proxy server service port name or number.
+  -> NS.HostName
+  -- ^ Destination server hostname or IP address. We connect to this host
+  -- /through/ the SOCKS5 proxy specified in the previous arguments.
+  --
+  -- Note that if hostname resolution on this 'NS.HostName' is necessary, it
+  -- will happen on the proxy side for security reasons, not locally.
+  -> NS.ServiceName -- ^ Destination server service port name or number.
+  -> ((NS.Socket, NS.SockAddr, NS.SockAddr) -> m r)
+  -- ^ Computation taking 'NS.Socket' connected to the SOCKS5 server (through
+  -- which we can interact with the destination server), the address of that
+  -- SOCKS5 server, and the address of the destination server, in that order.
+  -> m r
+connectSOCKS5 phn psn dhn dsn k =
+  connect phn psn $ \(psock, paddr) -> do
+     daddr <- connectSockSOCKS5 psock dhn dsn
+     k (psock, paddr, daddr)
+
 --------------------------------------------------------------------------------
 
 -- $server-side
@@ -191,8 +226,8 @@
 -- If you prefer to acquire and close the socket yourself, then use 'bindSock'
 -- and 'closeSock', as well as 'listenSock' function.
 --
--- Note: The 'NS.NoDelay' and 'NS.ReuseAddr' options are set on the socket. The
--- maximum number of incoming queued connections is 2048.
+-- Note: The 'NS.NoDelay', 'NS.KeepAlive' and 'NS.ReuseAddr' options are set on
+-- the socket. The maximum number of incoming queued connections is 2048.
 listen
   :: (MonadIO m, Ex.MonadMask m)
   => HostPreference -- ^ Host to bind.
@@ -266,6 +301,8 @@
 -- Prefer to use 'connect' if you will be using the socket within a limited
 -- scope and would like it to be closed immediately after its usage or in case
 -- of exceptions.
+--
+-- Note: The 'NS.NoDelay' and 'NS.KeepAlive' options are set on the socket.
 connectSock
   :: MonadIO m
   => NS.HostName -- ^ Server hostname or IP address.
@@ -286,9 +323,11 @@
       (x:xs) -> Ex.catch (useAddr x) (\(_ :: IOError) -> tryAddrs xs)
     useAddr :: NS.AddrInfo -> IO (NS.Socket, NS.SockAddr)
     useAddr addr = do
-       yx <- timeout 1000000 $ do -- 1 second
+       yx <- timeout 3000000 $ do -- 3 seconds
           Ex.bracketOnError (newSocket addr) closeSock $ \sock -> do
              let sockAddr = NS.addrAddress addr
+             NS.setSocketOption sock NS.NoDelay 1
+             NS.setSocketOption sock NS.KeepAlive 1
              NS.connect sock sockAddr
              pure (sock, sockAddr)
        case yx of
@@ -304,7 +343,8 @@
 -- within a limited scope, and would like it to be closed immediately after its
 -- usage or in case of exceptions.
 --
--- Note: The 'NS.NoDelay' and 'NS.ReuseAddr' options are set on the socket.
+-- Note: The 'NS.NoDelay', 'NS.KeepAlive' and 'NS.ReuseAddr' options are set on
+-- the socket.
 bindSock
   :: MonadIO m
   => HostPreference -- ^ Host to bind.
@@ -324,7 +364,7 @@
       , NS.addrSocketType = NS.Stream }
     tryAddrs :: [NS.AddrInfo] -> IO (NS.Socket, NS.SockAddr)
     tryAddrs = \case
-      [] -> fail "bindSock: No addresses available"
+      [] -> fail "Network.Simple.TCP.bindSock: No addresses available"
       [x] -> useAddr x
       (x:xs) -> Ex.catch (useAddr x) (\(_ :: IOError) -> tryAddrs xs)
     useAddr :: NS.AddrInfo -> IO (NS.Socket, NS.SockAddr)
@@ -332,6 +372,7 @@
       let sockAddr = NS.addrAddress addr
       NS.setSocketOption sock NS.NoDelay 1
       NS.setSocketOption sock NS.ReuseAddr 1
+      NS.setSocketOption sock NS.KeepAlive 1
       when (isIPv6addr addr) $ do
          NS.setSocketOption sock NS.IPv6Only (if hp == HostIPv6 then 1 else 0)
       NS.bind sock sockAddr
@@ -344,6 +385,45 @@
   Ex.catch (Ex.finally (NS.shutdown s NS.ShutdownBoth)
                        (NS.close s))
            (\(_ :: Ex.SomeException) -> pure ())
+
+--------------------------------------------------------------------------------
+
+-- | Given a 'NS.Socket' connected to a SOCKS5 proxy server, establish a
+-- connection to the specified destination server through that proxy.
+connectSockSOCKS5
+  :: MonadIO m
+  => NS.Socket
+  -- ^ Socket connected to the SOCKS5 proxy server.
+  --
+  -- After a successful use of 'connectSockSOCKS5', all traffic exchanged
+  -- through this socket will be between ourselves and the destination server.
+  -> NS.HostName
+  -- ^ Destination server hostname or IP address. We connect to this host
+  -- /through/ the SOCKS5 proxy specified in the previous arguments.
+  --
+  -- Note that if hostname resolution on this 'NS.HostName' is necessary, it
+  -- will happen on the proxy side for security reasons, not locally.
+  -> NS.ServiceName -- ^ Destination server service port name or number.
+  -> m NS.SockAddr -- ^ Address of the destination server.
+connectSockSOCKS5 psock dhn dsn = liftIO $ do
+   dpn :: NS.PortNumber <- do
+     pn <- getServicePortNumber' dsn
+     -- The @socks5@ library seems to be broken and use port numbers
+     -- in the wrong byte order. Here we work around that.
+     pure (fromIntegral (byteSwap16 (fromInteger (toInteger pn))))
+   let dsa = NS5.SocksAddress (NS5.SocksAddrDomainName (B8.pack dhn)) dpn
+   NS5.establish psock [NS5.SocksMethodNone] >>= \case
+     NS5.SocksMethodNone -> do
+       NS5.rpc_ psock (NS5.Connect dsa) >>= \case
+         (NS5.SocksAddrIPV4 ha, p) -> pure (NS.SockAddrInet p ha)
+         (NS5.SocksAddrIPV6 ha, p) -> pure (NS.SockAddrInet6 p 0 ha 0)
+         _ -> err "Impossible reply from SOCKS5 server"
+     r -> err ("Unsupported reply from SOCKS5 server: " ++ show r)
+ where
+   err :: String -> IO a
+   err s = fail ("Network.Simple.TCP.connectSockSOCKS5: " ++ s)
+
+
 
 --------------------------------------------------------------------------------
 -- Utils
