diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,11 @@
+## 1.1.1
+
+* Long-polling connections now emit a `ping` message after 45 seconds, if no
+  data is written to them.
+
+* There is a new `dupRawReader` function, which lets you create a read-only
+  stream of raw communication with a socket.
+
 ## 1.1.0
 
 * The `ServerAPI` functions `srvWriteBuilder`, `srvSetContentType` and
diff --git a/engine-io.cabal b/engine-io.cabal
--- a/engine-io.cabal
+++ b/engine-io.cabal
@@ -1,5 +1,5 @@
 name: engine-io
-version: 1.1.0
+version: 1.1.1
 synopsis: A Haskell implementation of Engine.IO
 homepage: http://github.com/ocharles/engine.io
 license: BSD3
@@ -27,7 +27,7 @@
    RankNTypes, OverloadedStrings, RecordWildCards
 
   build-depends:
-    aeson >=0.7 && <0.8,
+    aeson >=0.7 && <0.9,
     async >=2.0 && <2.1,
     attoparsec >=0.11 && <0.13,
     base >=4.6 && <4.8,
diff --git a/src/Network/EngineIO.hs b/src/Network/EngineIO.hs
--- a/src/Network/EngineIO.hs
+++ b/src/Network/EngineIO.hs
@@ -22,6 +22,7 @@
   , SocketId
   , socketId
   , getOpenSockets
+  , dupRawReader
 
     -- * The Engine.IO Protocol
     -- This section of the API is somewhat low-level, and exposes the raw
@@ -293,6 +294,7 @@
   , socketTransport :: STM.TVar Transport
   , socketIncomingMessages :: STM.TChan PacketContent
   , socketOutgoingMessages :: STM.TChan PacketContent
+  , socketRawIncomingBroadcast :: STM.TChan Packet
   }
 
 instance Eq Socket where
@@ -457,6 +459,7 @@
                    STM.newTVarIO transport)
            <*> STM.newTChanIO
            <*> STM.newTChanIO
+           <*> STM.newBroadcastTChanIO
 
   liftIO $ STM.atomically (STM.modifyTVar' (eioOpenSessions eio) (HashMap.insert sId socket))
 
@@ -478,6 +481,7 @@
                _ ->
                  return ()
 
+             STM.writeTChan (socketRawIncomingBroadcast socket) req
              return (Just req)
 
         , do STM.readTChan (socketOutgoingMessages socket)
@@ -496,7 +500,7 @@
     saOnDisconnect app
 
   let openMessage = OpenMessage { omSocketId = sId
-                                , omUpgrades = [ Websocket ]
+                                , omUpgrades = [ ]
                                 , omPingTimeout = 60000
                                 , omPingInterval = 25000
                                 }
@@ -588,11 +592,27 @@
   where
 
   poll = do
+    readTimeout <- liftIO $ STM.registerDelay (45 * 1000000)
+
     let out = transOut transport
-    packets <- liftIO $
-      (:) <$> STM.atomically (STM.readTChan out)
-          <*> unfoldM (STM.atomically (STM.tryReadTChan (transOut transport)))
 
+    -- Here we attempt to read as much from the transport output as we can.
+    -- We also consider the timeout above, such that if we haven't even read
+    -- one message by the timeout is reached, we instead emit a `ping`.
+    packets <- liftIO $ do
+      p <- STM.atomically $ do
+        let dequeueHead = Just <$> STM.readTChan out
+            timeout = Nothing <$ (STM.readTVar readTimeout >>= STM.check)
+
+        dequeueHead <|> timeout
+
+      case p of
+        Just p' ->
+          (p' :) <$> unfoldM (STM.atomically (STM.tryReadTChan (transOut transport)))
+
+        Nothing ->
+          return [ Packet Ping (BinaryPacket mempty) ]
+
     writeBytes api (encodePayload supportsBinary (Payload (V.fromList packets)))
 
   post = do
@@ -650,6 +670,21 @@
                    TransportUnknown -> "Transport unknown"
                    SessionIdUnknown -> "Session ID unknown"
                    BadRequest -> "Bad request"
+
+
+--------------------------------------------------------------------------------
+-- | Create a new 'IO' action to read the socket's raw incoming communications.
+-- The result of this call is iteslf an STM action, which will called will return
+-- the next unread incoming packet (or block). This provides you with a separate
+-- channel to monitor incoming communications. This may useful to monitor to
+-- determine if the socket has activity.
+--
+-- This is a fairly low level operation, so you will receive *all* packets -
+-- including pings and other control codes.
+dupRawReader :: Socket -> IO (STM.STM Packet)
+dupRawReader s = do
+  c <- STM.atomically (STM.dupTChan (socketRawIncomingBroadcast s))
+  return (STM.readTChan c)
 
 
 {- $intro
