суббота, 23 июля 2011 г.

Простой Network-клиент на Haskell

Чуть ранее мы написали простой TCP сервер. Теперь мы напишем простой клиент для него.


Наш клиент будет получать сообщение со стандартного входа (клавиатура), отправлять его на сервер и получать ответ. Все просто.

Уже знакомый нам импорт:
import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString (send, recv)
import qualified Data.ByteString.Char8 as B8
import System.Environment (getArgs)
И сам клиент:
client :: String -> Int -> IO ()
client host port = withSocketsDo $ do
                addrInfo <- getAddrInfo Nothing (Just host) (Just $ show port)
                let serverAddr = head addrInfo
                sock <- socket (addrFamily serverAddr) Stream defaultProtocol
                connect sock (addrAddress serverAddr)
                msgSender sock
                sClose sock

msgSender :: Socket -> IO ()
msgSender sock = do
  msg <- B8.getLine
  send sock msg
  rMsg <- recv sock 10
  B8.putStrLn rMsg
  if msg == B8.pack "q" then putStrLn "Disconnected!" else msgSender sock

Тут-то и комментировать нечего. Вопросы возникают только с этими строчками:                
addrInfo <- getAddrInfo Nothing (Just host) (Just $ show port)
let serverAddr = head addrInfo
sock <- socket (addrFamily serverAddr) Stream defaultProtocol

Функция getAddrInfo имеет следующую сигнатуру:
:: Maybe AddrInfo
выбранный тип сокета или протокол
-> Maybe HostNameHostname машины
-> Maybe ServiceName
имя службы (порт) для поиска
-> IO [AddrInfo]

Функция возвращает список, с лучший вариантом из найденного в начале. Либо пустой список.
Естественно нам нужен "лучший" вариант, поэтому мы не думая берем head:
let serverAddr = head addrInfo
Для общения мы должны быть на одной волне, а именно - мы должны использовать единый протокол.

Поэтому при создании сокета мы "вытягиваем" наименование протокола из найденного сервера:
sock <- socket (addrFamily serverAddr) Stream defaultProtocol
Для запуска нашего клиента вызываем функцию client:
client "localhost" 3000
Параметры естественно могут быть другими.

Функция main:
main = do
  [host', port'] <- getArgs
  client host' (read port' :: Int)
И компиляция:
ghc -o client Client.hs
Бинарник запускается так: client "localhost" 3000
Вот и все. Клиент готов!

Весь код тут.

Комментариев нет:

Отправить комментарий