Haskellで、TCPのサーバーを書いてみたいと思い、手始めにエコーサーバーを書いてみました。 エコーサーバーというのは、クライアントからの入力をそのまま返すサーバーです。 以下の記事を参考にもっと簡単なものを作ってみます。 A simple TCP server | The Haskell Sequence
main = withSocketsDo $ do
         [p] <- getArgs
         let port = fromIntegral (read p :: Int)
         soc <- listenOn $ PortNumber port
         putStrLn $ "start server, listening on: " ++ show port
         acceptLoop soc `finally` sClose soc
main部分です。 ソケットを開いてlistenします。
acceptLoop soc = do
  (hd, host, port) <- accept soc
  forkOS $ echoLoop hd
  acceptLoop soc
mainのあとの処理です。 クライアントがきたら、accept(受け付け)して、新しいスレッドを生成し、そちらに処理を任せます。 新しいクラアントに対応しつづけるため、処理が終わってはいけませんので、こちらは無限ループを続けます。
echoLoop hd = do
  sequence_ (repeat (do { -- ioアクションの無限リスト
                          l <- hGetLine hd;
                          hPutStrLn hd l;
                          hFlush hd
                     }))
  `catch` (\(SomeException e) -> return ())
  `finally` hClose hd
クライアントごとの処理です。 こちらは、一行読み込んで一行そのまま書き込むという処理を繰り返しているだけです。 さっそく実行してみましょう。
$ ghc -o echoHS -O --make -threaded echosimple.hs
$ ./echoHS 8080
start server, listening on: 8080
別のコンソールをひらきアクセスしてみます。
$ telnet localhost 8080
hoge
hoge
aiu
aiu


できました! 30行程度で、エコーサーバーが完成しました。 ついでにRubyでも同じものを書いたのでそちらも掲載しておきます。
module Main where
import Network
import Monad
import System.IO
import System.Environment (getArgs)


import Control.Exception
import Control.Concurrent
import Prelude hiding (catch)


main = withSocketsDo $ do
         [p] <- getArgs
         let port = fromIntegral (read p :: Int)
         soc <- listenOn $ PortNumber port
         putStrLn $ "start server, listening on: " ++ show port
         acceptLoop soc `finally` sClose soc


acceptLoop soc = do
  (hd, host, port) <- accept soc
  forkOS $ echoLoop hd
  acceptLoop soc


echoLoop hd = do
  sequence_ (repeat (do { -- ioアクションの無限リスト
                          l <- hGetLine hd;
                          hPutStrLn hd l;
                          hFlush hd
                     }))
  `catch` (\(SomeException e) -> return ())
  `finally` hClose hd

require 'socket'
def main
    unless ARGV[0]
        puts "usage: #{$0} PORTNUMBER"
        exit(true)
    end
    soc = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
    sockaddr = Socket.sockaddr_in(ARGV[0].to_i, "0.0.0.0")
    soc.bind(sockaddr)
    soc.listen(5)
    puts "start server, listening on #{ARGV[0]}"
    accept_do(soc)
end

def accept_do(serv)
    while(true)
        soc, addr = serv.accept
        puts "new client"
        Thread.new(soc, &self::method(:echo_do))
    end
end
def echo_do(soc)
    while true
        buf = soc.gets
        puts "data: #{buf}"
        soc.puts(buf)
    end
end

main if($0==__FILE__)