socket-0.5.0.0: A portable and extensible sockets library.

Copyright(c) Lars Petersen 2015
LicenseMIT
Maintainerinfo@lars-petersen.net
Stabilityexperimental
Safe HaskellNone
LanguageHaskell2010

System.Socket

Contents

Description

This starts a TCP server on localhost, sends "Hello world!" to connecting peers and closes the connection immediately.

{-# LANGUAGE OverloadedStrings #-}
module Main where

import System.Socket
import System.Socket.Family.Inet as Inet
import Data.Monoid
import Data.ByteString
import Control.Monad
import Control.Concurrent
import Control.Exception

main :: IO ()
main = do
  s <- socket :: IO (Socket Inet Stream TCP)
  setSocketOption s (ReuseAddress True)
  bind s addr
  listen s 5
  forever $ do
    (peer,_) <- accept s
    forkIO $ do
      sendAll peer "Hello world!" mempty `finally` close peer
  where
    addr = SocketAddressInet Inet.loopback 8080

This downloads the [Haskell website](http://www.haskell.org) and prints it to stdout. Note the use of IPv4-mapped Inet6 addresses: This will work even if you don't have IPv6 connectivity yet and is the preferred method when writing new applications.

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.Monoid
import Data.ByteString.Lazy as B
import System.Socket

main :: IO ()
main = do
  withConnectedSocket "www.haskell.org" "80" (aiAll `mappend` aiV4Mapped) $ \sock-> do
    let _ = sock :: Socket Inet6 Stream TCP
    sendAll sock "GET / HTTP/1.0\r\nHost: www.haskell.org\r\n\r\n" mempty
    x <- receiveAll sock (1024*1024*1024) mempty
    B.putStr x

Synopsis

Name Resolution

getAddressInfo

class Family f => GetAddressInfo f where Source

Methods

getAddressInfo :: (Type t, Protocol p) => Maybe ByteString -> Maybe ByteString -> AddressInfoFlags -> IO [AddressInfo f t p] Source

Maps names to addresses (i.e. by DNS lookup).

The operation throws AddressInfoExceptions.

Contrary to the underlying getaddrinfo operation this wrapper is typesafe and thus only returns records that match the address, type and protocol encoded in the type. This is the price we have to pay for typesafe sockets and extensibility.

If you need different types of records, you need to start several queries. If you want to connect to both IPv4 and IPV6 addresses use aiV4Mapped and use IPv6-sockets.

> getAddressInfo (Just "www.haskell.org") (Just "80") aiV4Mapped :: IO [AddressInfo Inet6 Stream TCP]
[AddressInfo {
   addressInfoFlags = AddressInfoFlags 8,
   socketAddress    = SocketAddressInet6 {address = 2400:cb00:2048:0001:0000:0000:6ca2:cc3c, port = 80, flowInfo = mempty, scopeId = 0},
   canonicalName    = Nothing }]
> getAddressInfo (Just "darcs.haskell.org") Nothing aiV4Mapped :: IO [AddressInfo Inet6 Stream TCP]
[AddressInfo {
   addressInfoFlags = AddressInfoFlags 8, 
   socketAddress    = SocketAddressInet6 {address = 0000:0000:0000:0000:0000:ffff:17fd:e1ad, port = 0, flowInfo = mempty, scopeId = 0},
   canonicalName    = Nothing }]
> getAddressInfo (Just "darcs.haskell.org") Nothing mempty :: IO [AddressInfo Inet6 Stream TCP]
*** Exception: AddressInfoException "Name or service not known"

getNameInfo

class Family f => GetNameInfo f where Source

Maps addresss to readable host- and service names.

The operation throws AddressInfoExceptions.

> getNameInfo (SocketAddressInet loopback 80) mempty
("localhost.localdomain","http")

Operations

socket

socket :: (Family f, Type t, Protocol p) => IO (Socket f t p) Source

Creates a new socket.

Whereas the underlying POSIX socket operation takes 3 parameters, this library encodes this information in the type variables. This rules out several kinds of errors and escpecially simplifies the handling of addresses (by using associated type families). Examples:

-- create a IPv4-UDP-datagram socket
sock <- socket :: IO (Socket Inet Datagram UDP)
-- create a IPv6-TCP-streaming socket
sock6 <- socket :: IO (Socket Inet6 Stream TCP)
  • This operation sets up a finalizer that automatically closes the socket when the garbage collection decides to collect it. This is just a fail-safe. You might still run out of file descriptors as there's no guarantee about when the finalizer is run. You're advised to manually close the socket when it's no longer needed. If possible, use bracket to reliably close the socket descriptor on exception or regular termination of your computation:

    result <- bracket (socket :: IO (Socket Inet6 Stream TCP)) close $ \sock-> do
      somethingWith sock -- your computation here
      return somethingelse
  • This operation configures the socket non-blocking to work seamlessly with the runtime system's event notification mechanism.
  • This operation can safely deal with asynchronous exceptions without leaking file descriptors.
  • This operation throws SocketExceptions. Consult your man page for details and specific errnos.

connect

connect :: Family f => Socket f t p -> SocketAddress f -> IO () Source

Connects to an remote address.

  • Calling connect on a closed socket throws eBadFileDescriptor even if the former file descriptor has been reassigned.
  • This operation returns as soon as a connection has been established (as if the socket were blocking). The connection attempt has either failed or succeeded after this operation threw an exception or returned.
  • The operation might throw SocketExceptions. Due to implementation quirks the socket should be considered in an undefined state when this operation failed. It should be closed then.
  • Also see [these considerations](http:/cr.yp.todocs/connect.html) on the problems with connecting non-blocking sockets.

bind

bind :: Family f => Socket f t p -> SocketAddress f -> IO () Source

Bind a socket to an address.

  • Calling bind on a closed socket throws eBadFileDescriptor even if the former file descriptor has been reassigned.
  • It is assumed that c_bind never blocks and therefore eInProgress, eAlready and eInterrupted don't occur. This assumption is supported by the fact that the Linux manpage doesn't mention any of these errors, the Posix manpage doesn't mention the last one and even MacOS' implementation will never fail with any of these when the socket is configured non-blocking as [argued here](http:/stackoverflow.coma/14485305).
  • This operation throws SocketExceptions. Consult your man page for details and specific errnos.

listen

listen :: Socket f t p -> Int -> IO () Source

Starts listening and queueing connection requests on a connection-mode socket.

  • Calling listen on a closed socket throws eBadFileDescriptor even if the former file descriptor has been reassigned.
  • The second parameter is called backlog and sets a limit on how many unaccepted connections the socket implementation shall queue. A value of 0 leaves the decision to the implementation.
  • This operation throws SocketExceptions. Consult your man page for details and specific errnos.

accept

accept :: Family f => Socket f t p -> IO (Socket f t p, SocketAddress f) Source

Accept a new connection.

  • Calling accept on a closed socket throws eBadFileDescriptor even if the former file descriptor has been reassigned.
  • This operation configures the new socket non-blocking (TODO: use accept4 if available).
  • This operation sets up a finalizer for the new socket that automatically closes the new socket when the garbage collection decides to collect it. This is just a fail-safe. You might still run out of file descriptors as there's no guarantee about when the finalizer is run. You're advised to manually close the socket when it's no longer needed.
  • This operation throws SocketExceptions. Consult your man page for details and specific errnos.
  • This operation catches eAgain, eWouldBlock and eInterrupted internally and retries automatically.

send, sendTo

send :: Socket f t p -> ByteString -> MessageFlags -> IO Int Source

Send a message on a connected socket.

sendTo :: Family f => Socket f t p -> ByteString -> MessageFlags -> SocketAddress f -> IO Int Source

Like send, but allows for specifying a destination address.

receive, receiveFrom

receive :: Socket f t p -> Int -> MessageFlags -> IO ByteString Source

Receive a message on a connected socket.

  • Calling receive on a closed socket throws eBadFileDescriptor even if the former file descriptor has been reassigned.
  • The operation takes a buffer size in bytes a first parameter which limits the maximum length of the returned ByteString.
  • This operation throws SocketExceptions. Consult man 3p receive for details and specific errnos.
  • eAgain, eWouldBlock and eInterrupted and handled internally and won't be thrown. For performance reasons the operation first tries a read on the socket and then waits when it got eAgain or eWouldBlock.

receiveFrom :: Family f => Socket f t p -> Int -> MessageFlags -> IO (ByteString, SocketAddress f) Source

Like receive, but additionally yields the peer address.

close

close :: Socket f t p -> IO () Source

Closes a socket.

  • This operation is idempotent and thus can be performed more than once without throwing an exception. If it throws an exception it is presumably a not recoverable situation and the process should exit.
  • This operation does not block.
  • This operation wakes up all threads that are currently blocking on this socket. All other threads are guaranteed not to block on operations on this socket in the future. Threads that perform operations other than close on this socket will fail with eBadFileDescriptor after the socket has been closed (close replaces the Fd in the MVar with -1 to reliably avoid use-after-free situations).
  • This operation potentially throws SocketExceptions (only EIO is documented). eInterrupted is catched internally and retried automatically, so won't be thrown.

Convenience Operations

withConnectedSocket

withConnectedSocket :: forall f t p a. (GetAddressInfo f, Type t, Protocol p) => ByteString -> ByteString -> AddressInfoFlags -> (Socket f t p -> IO a) -> IO a Source

Looks up a name and executes an supplied action with a connected socket.

  • The addresses returned by getAddressInfo are tried in sequence until a connection has been established or all have been tried.
  • If connect fails on all addresses the exception that occured on the last connection attempt is thrown.
  • The supplied action is executed at most once with the first established connection.
  • If the address family is Inet6, V6Only is set to False which means the other end may be both IPv4 or IPv6.
  • All sockets created by this operation get closed automatically.
  • This operation throws AddressInfoExceptions, SocketExceptions and all exceptions that that the supplied action might throw.
withConnectedSocket "wwww.haskell.org" "80" (aiAll `mappend` aiV4Mapped) $ \sock-> do
  let _ = sock :: Socket Inet6 Stream TCP
  doSomethingWithSocket sock

sendAll

sendAll :: Socket f Stream p -> ByteString -> MessageFlags -> IO () Source

Like send, but operates on lazy ByteStrings and continues until all data has been sent or an exception occured.

receiveAll

receiveAll :: Socket f Stream p -> Int64 -> MessageFlags -> IO ByteString Source

Like receive, but operates on lazy ByteStrings and continues until either an empty part has been received (peer closed the connection) or given buffer limit has been exceeded or an exception occured.

  • The Int64 parameter is a soft limit on how many bytes to receive. Collection is stopped if the limit has been exceeded. The result might be up to one internal buffer size longer than the given limit. If the returned ByteStrings length is lower or eqal than the limit, the data has not been truncated and the transmission is complete.

Sockets

newtype Socket f t p Source

A generic socket type. Also see socket for details.

The socket is just an MVar-wrapped file descriptor. It is exposed in order to make this library easily extensible, but it is usually not necessary nor advised to work directly on the file descriptor. If you do, the following rules must be obeyed:

  • Make sure not to deadlock. Use withMVar or similar.
  • The lock must not be held during a blocking call. This would make it impossible to send and receive simultaneously or to close the socket.
  • The lock must be held when calling operations that use the file descriptor. Otherwise the socket might get closed or even reused by another thread/capability which might result in reading from or writing totally different connection. This is a security nightmare!
  • The socket is non-blocking and all the code relies on that assumption. You need to use GHC's eventing mechanism primitives to block until something happens. The former rules forbid to use threadWaitRead as it does not seperate between registering the file descriptor (for which the lock must be held) and the actual waiting (for which you must not hold the lock). Also see [this](https:/mail.haskell.orgpipermailhaskell-cafe2014-September/115823.html) thread and read the library code to see how the problem is currently circumvented.

Constructors

Socket (MVar Fd) 

Families

class Storable (SocketAddress f) => Family f where Source

Associated Types

type SocketAddress f Source

Methods

familyNumber :: f -> CInt Source

Instances

Inet

Inet6

Types

class Type t where Source

Methods

typeNumber :: t -> CInt Source

Datagram

data Datagram Source

Instances

Raw

data Raw Source

Instances

SequentialPacket

Stream

data Stream Source

Instances

Protocols

class Protocol p where Source

Methods

protocolNumber :: p -> CInt Source

Instances

UDP

data UDP Source

Instances

TCP

data TCP Source

Instances

Exceptions

SocketException

AddressInfoException

newtype AddressInfoException Source

Contains the error code that can be matched against. Use show to get a human readable explanation of the error.

eaiAgain :: AddressInfoException Source

AddressInfoException "Temporary failure in name resolution"

eaiBadFlags :: AddressInfoException Source

AddressInfoException "Bad value for ai_flags"

eaiFail :: AddressInfoException Source

AddressInfoException "Non-recoverable failure in name resolution"

eaiFamily :: AddressInfoException Source

AddressInfoException "ai_family not supported"

eaiMemory :: AddressInfoException Source

AddressInfoException "Memory allocation failure"

eaiNoName :: AddressInfoException Source

AddressInfoException "No such host is known"

eaiSocketType :: AddressInfoException Source

AddressInfoException "ai_socktype not supported"

eaiService :: AddressInfoException Source

AddressInfoException "Servname not supported for ai_socktype"

eaiSystem :: AddressInfoException Source

AddressInfoException "System error"

Socket Options

getSocketOption

setSocketOption

data Error Source

SO_ERROR

Constructors

Error SocketException 

Flags

MessageFlags

newtype MessageFlags Source

Use the Monoid instance to combine several flags:

mconcat [msgNoSignal, msgWaitAll]

Use the Bits instance to check whether a flag is set:

if flags .&. msgEndOfRecord /= mempty then ...

Constructors

MessageFlags CInt 

AddressInfoFlags

newtype AddressInfoFlags Source

Use the Monoid instance to combine several flags:

mconcat [aiAddressConfig, aiV4Mapped]

Constructors

AddressInfoFlags CInt 

aiAll :: AddressInfoFlags Source

AI_ALL: Return both IPv4 (as mapped SocketAddressInet6) and IPv6 addresses when aiV4Mapped is set independent of whether IPv6 addresses exist for this name.

aiV4Mapped :: AddressInfoFlags Source

AI_V4MAPPED: Return mapped IPv4 addresses if no IPv6 addresses could be found or if aiAll flag is set.

NameInfoFlags

newtype NameInfoFlags Source

Use the Monoid instance to combine several flags:

mconcat [niNameRequired, niNoFullyQualifiedDomainName]

Constructors

NameInfoFlags CInt 

niNameRequired :: NameInfoFlags Source

NI_NAMEREQD: Throw an exception if the hostname cannot be determined.

niDatagram :: NameInfoFlags Source

NI_DGRAM: Service is datagram based (i.e. UDP) rather than stream based (i.e. TCP).

niNoFullyQualifiedDomainName :: NameInfoFlags Source

NI_NOFQDN: Return only the hostname part of the fully qualified domain name for local hosts.

niNumericHost :: NameInfoFlags Source

NI_NUMERICHOST: Return the numeric form of the host address.

niNumericService :: NameInfoFlags Source

NI_NUMERICSERV: Return the numeric form of the service address.