2016-06-19 17 views
0

Ich schreibe einen Socket-Server mit runTCPServer von conduit-extra (früher bekannt als network-conduit). Mein Ziel ist es, mit meinem Editor über diesen Server zu interagieren --- den Server vom Editor aus zu aktivieren (höchstwahrscheinlich nur durch Aufruf eines externen Befehls), ihn zu benutzen und den Server zu beenden, wenn die Arbeit erledigt ist.Wie kann ich "runTCPServer" herunterfahren?

Der Einfachheit halber beginne ich mit einem einfachen Echo-Server, und sagen wir, ich möchte den gesamten Prozess herunterfahren, wenn die Verbindung geschlossen wird.

Also habe ich versucht:

{-# 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 

Aber das funktioniert nicht - das Programm weiterhin neue Verbindungen zu akzeptieren. Wenn ich mich nicht irre, liegt das daran, dass der Thread, der die Verbindung abhört, mit der wir es zu tun haben, mit exitSuccess ausgeht, aber der gesamte Prozess nicht. Das ist völlig verständlich, aber ich war nicht in der Lage, einen Weg zu finden, den gesamten Prozess zu beenden.

Wie kann ich einen Server beenden, der von runTCPServer ausgeführt wird? Ist runTCPServer etwas, das für immer dienen soll?

+0

'exit' aus dem Haupt-Thread aufgerufen werden muss (ich bin nicht sicher, warum, aber das ist die Anforderung). Sie können "runTCPServer" immer in einem separaten 'forkIO'd-Thread ausführen und den Hauptthread auf einige' MVar' warten lassen, die der Worker-Thread festlegt, wenn eine Beendigungsnachricht eintrifft. –

+0

Und ja, 'runTCPServer' soll für immer laufen, siehe [Quelle] (https://hackage.haskell.org/package/streaming-commons-0.1.15.5/docs/src/Data-Streaming-Network.html# runTCPServerWithHandle). –

+0

@ n.m. Das scheint die genaue Antwort zu sein (besonders 'forkIO' und' MVar'), nach der ich gesucht habe. Würden Sie ein einfaches Beispiel hinzufügen (wenn es Ihnen nichts ausmacht) und das als Antwort posten? – Yosh

Antwort

2

Hier ist eine einfache Umsetzung der Idee in den Kommentaren beschrieben:

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 
Verwandte Themen