2016-06-19 33 views
0

我在寫一個套接字服務器runTCPServerconduit-extra(以前叫做network-conduit)。我的目標是使用此服務器與我的編輯器進行交互 - 從編輯器激活服務器(很可能只是通過調用外部命令),使用它並在工作完成時終止服務器。如何關閉`runTCPServer`?

爲簡單起見,我從一個簡單的echo服務器開始,假設我想在連接關閉時關閉整個過程。

所以,我想:

{-# LANGUAGE OverloadedStrings #-} 
module Main where 
import Data.Conduit 
import Data.Conduit.Network 
import Data.ByteString (ByteString) 
import Control.Monad.IO.Class (liftIO) 
import System.Exit (exitSuccess) 
import Control.Exception 

defaultPort :: Int 
defaultPort = 4567 
main :: IO() 
main = runTCPServer (serverSettings defaultPort "*") $ \ appData -> 
     appSource appData $$ conduit =$= appSink appData 

conduit :: ConduitM ByteString ByteString IO() 
conduit = do 
    msg <- await 
    case msg of 
     Nothing -> liftIO $ do 
      putStrLn "Nothing left" 
      exitSuccess 
      -- I'd like the server to shut down here 
     (Just s) -> do 
      yield s 
      conduit 

但是,這並不工作 - 程序繼續接受新的連接。如果我沒有弄錯,這是因爲偵聽我們正在處理的連接的線程以exitSuccess退出,但整個過程不會。所以這是完全可以理解的,但我一直無法找到一種方法來退出整個過程。

如何終止由runTCPServer運行的服務器?是runTCPServer應該永遠服務的東西?

+0

'exit'必須從主線程調用(我不知道爲什麼,但是這是要求)。你總是可以在一個單獨的'forkIO'd線程中運行'runTCPServer',讓主線程等待一些工作線程在終止消息到達時設置的'MVar'。 –

+0

是的,'runTCPServer'旨在永遠運行,請參見[source](https://hackage.haskell.org/package/streaming-commons-0.1.15.5/docs/src/Data-Streaming-Network.html# runTCPServerWithHandle)。 –

+0

@ n.m。這似乎是我正在尋找的確切答案(尤其是'forkIO'和'MVar'部分)。你會添加一個簡單的例子(如果你不介意),並將其作爲答案發布? – Yosh

回答

2

這裏有一個簡單的實現在註釋中描述的想法:

main = do 
    mv <- newEmptyMVar 
    tid <- forkTCPServer (serverSettings defaultPort "*") $ \ appData -> 
     appSource appData $$ conduit mv =$= appSink appData 
    () <- takeMVar mv -- < -- wait for done signal 
    return() 

conduit :: MVar() -> ConduitM ByteString ByteString IO() 
conduit mv = do 
    msg <- await 
    case msg of 
     Nothing -> liftIO $ do 
      putStrLn "Nothing left" 
      putMVar mv() -- < -- signal that we're done 
     (Just s) -> do 
      yield s 
      conduit mv