multithreading - 如何在Websocket中使用超时,同时又不影响应用程序安全性?

标签 multithreading haskell asynchronous concurrency websocket

我正在用Websockets在Haskell中编写一个游戏服务器。我需要为玩家 Action 计时,并用超时 Action 更新游戏,因为套接字在指定的时间内没有收到来自客户端的 Action 。

我遇到了一个零星的问题,有时执行超时会导致抛出ConnectionClosed异常。如果未捕获到此异常,则该异常会杀死线程的套接字并断开客户端的连接。

但是,即使我按照客户端套接字下面的代码捕获了异常,也仍然未连接。这让我感到困惑。

我在使用的websockets库的跟踪器上发现了类似的问题:

If a thread is killed while receiveData or similar is blocking, the TCP connection is closed and a ConnectionClosed exception is thrown. https://github.com/jaspervdj/websockets/issues/101


handleSocketMsg :: MsgHandlerConfig -> MsgIn -> IO ()
handleSocketMsg msgHandlerConfig@MsgHandlerConfig {..} msg = do
  print $ "parsed msg: " ++ show msg
  msgOutE <- runExceptT $ runReaderT (gameMsgHandler msg) msgHandlerConfig
  either
    (\err -> sendMsg clientConn $ ErrMsg err)
    (handleNewGameState serverStateTVar)
    msgOutE


-- This function processes msgs from authenticated clients 
authenticatedMsgLoop :: MsgHandlerConfig -> IO ()
authenticatedMsgLoop msgHandlerConfig@MsgHandlerConfig {..}
 = do
  (catch
     (forever $ do
        msg <- WS.receiveData clientConn
        print msg
        let parsedMsg = parseMsgFromJSON msg
        print parsedMsg
        for_ parsedMsg $ handleSocketMsg msgHandlerConfig
        return ())
     (\e -> do
        let err = show (e :: WS.ConnectionException)
        print
          ("Warning: Exception occured in authenticatedMsgLoop for " ++
           show username ++ ": " ++ err)
        return ()))

-- takes a channel and if the player in the thread is the current player to act in the room 
-- then if no valid game action is received within 30 secs then we run the Timeout action
-- against the game
tableReceiveMsgLoop :: TableName -> TChan MsgOut -> MsgHandlerConfig -> IO ()
tableReceiveMsgLoop tableName channel msgHandlerConfig@MsgHandlerConfig {..} =
  forever $ do
    print "tableReceiveMsgLoop"
    dupChan <- atomically $ dupTChan channel
    chanMsg <- atomically $ readTChan dupChan
    sendMsg clientConn chanMsg
    if True
      then let timeoutMsg = GameMove tableName Timeout
               timeoutDuration = 5000000 -- 5 seconds for player to act
            in runTimedMsg timeoutDuration msgHandlerConfig tableName timeoutMsg
      else return ()

catchE :: TableName -> WS.ConnectionException -> IO MsgIn
catchE tableName e = do
  print e
  return $ GameMove tableName Timeout

-- Forks a new thread to run the timeout in then updates the game state 
-- with either the resulting timeout or player action
runTimedMsg :: Int -> MsgHandlerConfig -> TableName -> MsgIn -> IO ()
runTimedMsg duration msgHandlerConfig tableName timeoutMsg =
  withAsync
    (catch
       (awaitTimedMsg duration msgHandlerConfig tableName timeoutMsg)
       (catchE tableName)) $ \timedAction -> do
    playerActionE <- waitCatch timedAction
    let playerAction = fromRight timeoutMsg playerActionE
    handleSocketMsg msgHandlerConfig playerAction
    return ()

-- If the timeout occurs then we return the default msg 
awaitTimedMsg :: Int -> MsgHandlerConfig -> TableName -> MsgIn -> IO MsgIn
awaitTimedMsg duration msgHandlerConfig@MsgHandlerConfig {..} tableName defaultMsg = do
  maybeMsg <- timeout duration (WS.receiveData clientConn)
  return $ maybe defaultMsg parseWithDefaultMsg maybeMsg
  where
    timeoutDuration = 5000000
    parseWithDefaultMsg = (fromMaybe defaultMsg) . parseMsgFromJSON

最佳答案

通常,当网络库引发某种异常以指示连接已关闭时,这与您控件之外的事件有关。客户端由于某种原因断开连接-例如,也许他们关闭了计算机。

您将收到有关该事实的通知,因此您可以做出适当的 react ,例如,清理分配给该连接的服务器端资源,或者通过警告其他可能关心此问题的客户端。您无法重新打开客户端的计算机并强制他们继续使用您的服务。连接已关闭,您无能为力。

您描述的行为与您引用的文档完全匹配。 TCP连接已关闭,然后您收到异常,让您可以根据自己的喜好进行响应。您认为应该发生什么变化?

关于multithreading - 如何在Websocket中使用超时,同时又不影响应用程序安全性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51236380/

相关文章:

haskell - 如何在普遍量化的自由单子(monad)上进行模式匹配?

c# - Microsoft的异步服务器套接字示例

C# 异步服务器/客户端架构

python - PyQt:计时器无法从另一个线程启动

c++ - 混合 C++11 原子和 OpenMP

haskell - 输入函数的签名来查找向量的大小

haskell - 移动字符串中的元素

ios - 完成处理程序?异步调度?

java - 线程如何知道前面有join方法

multithreading - 在主线程上等待回调方法