从Haskell中的无限循环(C程序)获取每个换行符的数据

时间:2018-11-25 12:22:24

标签: haskell websocket yesod

我很难从标准输出获取每个换行符的数据。数据由C程序产生。这是C代码:

// gcc counter.c -o counter

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  unsigned int i = 0;
  while(1) {
    printf("%d\n", i);
    sleep(1);
    i++;
  }
}

我的目标是获得与下面的haskell函数相同的行为:

timeSource :: MonadIO m => Source m TL.Text
timeSource = forever $ do
    now <- liftIO getCurrentTime
    yield $ TL.pack $ show now
    liftIO $ threadDelay 1000000

我尝试使用readProcess模块中的readCreateProcessSystem.Process。这是我的尝试之一:

counter :: MonadIO m => Source m TL.Text
counter = do
    r <- liftIO $ readCreateProcess (proc "./counter" []) ""
    -- r <- liftIO $ readProcess "./counter" [] [] 
    yield $ TL.pack $ show r
    liftIO $ threadDelay 1000000

这就是我在counter中使用webSockets函数的方式:

    webSockets $ race_
        (sourceWS $$ Data.Conduit.List.map TL.toUpper =$ sinkWSText)
        -- (timeSource $$ sinkWSText)
        (counter $$ sinkWSText)

当我打开http://localhost:3000/时,它不起作用。这是完整的代码。

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}

module Main where

import Yesod.Core
import Yesod.WebSockets
import qualified Data.Text.Lazy as TL
import Control.Monad (forever)
import Control.Concurrent (threadDelay)
import Data.Time
import Data.Conduit
import System.Process 
import qualified Data.Conduit.List

data App = App

instance Yesod App

mkYesod "App" [parseRoutes|
/ HomeR GET
|]

timeSource :: MonadIO m => Source m TL.Text
timeSource = forever $ do
    now <- liftIO getCurrentTime
    yield $ TL.pack $ show now
    liftIO $ threadDelay 1000000

counter :: MonadIO m => Source m TL.Text
counter = do
  r <- liftIO $ readCreateProcess (proc "./counter" []) ""
  -- r <- liftIO $ readProcess "./counter" [] [] 
  yield $ TL.pack $ show r
  liftIO $ threadDelay 1000000

getHomeR :: Handler Html
getHomeR = do
    webSockets $ race_
        (sourceWS $$ Data.Conduit.List.map TL.toUpper =$ sinkWSText)
        (timeSource $$ sinkWSText)
        -- (counter $$ sinkWSText)
    defaultLayout $
        toWidget
            [julius|
                var conn = new WebSocket("ws://localhost:3000/");
                conn.onopen = function() {
                    document.write("<p>open!</p>");
                    document.write("<button id=button>Send another message</button>")
                    document.getElementById("button").addEventListener("click", function(){
                        var msg = prompt("Enter a message for the server");
                        conn.send(msg);
                    });
                    conn.send("hello world");
                };
                conn.onmessage = function(e) {
                    document.write("<p>" + e.data + "</p>");
                };
                conn.onclose = function () {
                    document.write("<p>Connection Closed</p>");
                };
            |]

main :: IO ()
main = warp 3000 App

所以我的问题是如何无限循环地访问每个printf数据并在Haskell中使用它?

编辑1:

根据MathematicalOrchid的建议,这是我到目前为止所做的。

counter :: MonadIO m => Source m TL.Text
counter = do
  r <- liftIO $ createProcess (proc "./counter" []){ std_out = CreatePipe, std_in = CreatePipe}
  let (Just inp, Just outp, _, phandle) = r
  liftIO $ hSetBuffering outp LineBuffering
  contents <- liftIO $ hGetLine outp
  yield $ TL.pack $ show contents
  liftIO $ threadDelay 1000000

我想在进程终止之前它仍然会阻塞。

编辑2:

为了测试createProcess是否有效,我尝试了此操作。

counterTest :: IO ()
counterTest = do
  r <- createProcess (proc "./counter" []){ std_out = CreatePipe, std_in = CreatePipe}
  let (Just inp, Just outp, _, phandle) = r
  hSetBuffering outp LineBuffering
  contents <- hGetLine outp
  print contents

显然它仍在阻止。

2 个答案:

答案 0 :(得分:2)

引用readProcess的文档:

  

readProcess派生一个外部进程,严格读取其标准输出,阻塞直到该进程终止,然后返回输出字符串。外部过程会继承标准错误。

(请注意。)看来readCreateProcess的工作方式与此类似。

因此,基本上,当您调用此函数时,它将永远坐在那里等待您的外部进程退出。

我建议您像以前一样使用proc创建一个CreateProcess结构,将std_in更改为CreatePipe,然后调用createProcess,这应该返回您您可以根据需要hGetLine进行处理的句柄。

答案 1 :(得分:1)

从这个answer开始,我必须将content添加到我的C文件中。

这是我的解决方案:

rendered

这是我在Haskell中阅读过程的方式:

fflush(stdout);

enter image description here