Haskell でソケット通信をやってみる - yasuabe blog
このエントリを見て、簡単そうだったので遊んでみました。
httpでGETをするだけですが、こんな感じで書けました。
import Network import System.IO getCommand :: String -> IO String getCommand hostName = withSocketsDo $ do hSetBuffering stdout NoBuffering soc <- connectTo hostName (PortNumber 80) hSetBuffering soc LineBuffering hPutStr soc $ "GET /index.html HTTP/1.1\nHost:" ++ hostName ++ "\n\n" result <- hGetContents soc length result `seq` hClose soc hClose soc return result main = getCommand "www.boost.org" >>= putStrLn
HTTP/1.1 200 OK Date: Wed, 04 Jan 2012 09:05:47 GMT Server: Apache/2.0.52 (Red Hat) Accept-Ranges: bytes Transfer-Encoding: chunked Content-Type: text/html 38b <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <meta name="generator" content= "HTML Tidy for Windows (vers 1st November 2003), see www.w3.org" /> <title>Boost C++ Libraries</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="icon" href="/favicon.ico" type="image/ico" /> <link rel="stylesheet" type="text/css" href="/style-v2/section-welcome.css" /> <!--[if IE 7]> <style type="text/css"> body { behavior: url(/style-v2/csshover3.htc); } </style> <![endif]--> <meta name="google-site-verification" content="mpr2HgFpodnbF_8fv4qXd9roIClVwtX3C-Kd3F6r61w" /> </head> <!-- Note: Editing website content is documented at: http://www.boost.org/development/website_updating.html --> ...
いろいろ試行錯誤しました。
まず、名前解決はどうやるのかを調べたら、connectToの中でやってたので、URLを直指定でOKでした。
それと、受信したデータをどうやって関数の戻りにするかで悩みました。hGetContentsで受信したデータは遅延評価されてるので、接続を閉じてしまうとうまくデータがとれなくなってしまいます。
なので、hGetContentsしたデータにlength関数をかませてeofまで評価して、それから接続を閉じています。
それと、ここではエラー処理を書いていませんが、接続に失敗したらconnectToは例外を投げます。catchしましょう。
日本語はまだ表示できていません。
参考:
Network - Hackage
Network.Socket - Hackage
haskell hostname resolve - 今日も明日もググったー
hGetContents と hClose - zknxの日記
Haskell でネットワーク - zyxwvの日記
追記 2012/01/05 15:11:
shelarcyさんからアドバイスいただいたので、deepseqで書き換えてみました。
@cpp_akira hGetContents したデータを取得するには、length 関数ではなく deepseq パッケージの Control.Depseq モジュールで提供されている関数を使うと良いですよ。 URL 2012-01-05 15:02:19 via YoruFukurou to @cpp_akira |
2012-01-05 15:03:45 via YoruFukurou to @cpp_akira |
@cpp_akira deepseq パッケージは Haskell Platform で提供されているので、利用するのにそんなに手間もかかりませんし。 URL 2012-01-05 15:07:11 via YoruFukurou to @cpp_akira |
import Network import System.IO import Control.DeepSeq getCommand :: String -> IO String getCommand hostName = withSocketsDo $ do hSetBuffering stdout NoBuffering soc <- connectTo hostName (PortNumber 80) hSetBuffering soc LineBuffering hPutStr soc $ "GET /index.html HTTP/1.1\nHost:" ++ hostName ++ "\n\n" result <- hGetContents soc result `deepseq` hClose soc return result main = getCommand "www.boost.org" >>= putStrLn