[project @ 1999-07-29 13:57:34 by sof]
[ghc-hetmet.git] / ghc / lib / misc / Socket.lhs
1 %
2 % (c) The GRASP/AQUA Project, Glasgow University, 1995-98
3 %
4 % Last Modified: Fri Jul 21 15:53:32 1995
5 % Darren J Moffat <moffatd@dcs.gla.ac.uk>
6 %
7 % Further hacked on by Sigbjorn Finne <sof@dcs.gla.ac.uk>
8 %
9 \section[Socket]{Haskell 1.3 Socket bindings}
10
11
12 \begin{code}       
13 {-# OPTIONS -#include "cbits/ghcSockets.h" #-}
14
15 #include "config.h"
16
17 module Socket (
18         PortID(..),
19         Hostname,
20
21         connectTo,      -- :: Hostname -> PortID -> IO Handle
22         listenOn,       -- :: PortID -> IO Socket
23         
24         accept,         -- :: Socket -> IO (Handle, HostName)
25
26         sendTo,         -- :: Hostname -> PortID -> String -> IO ()
27         recvFrom,       -- :: Hostname -> PortID -> IO String
28
29         socketPort,     -- :: Socket -> IO PortID
30         
31         withSocketsDo,  -- :: IO a   -> IO a
32         
33         PortNumber,
34         mkPortNumber    -- :: Int    -> PortNumber
35
36        ) where
37
38 import BSD
39 import SocketPrim hiding ( accept, socketPort, recvFrom, sendTo )
40 import qualified SocketPrim ( accept, socketPort )
41 import IO
42 \end{code} 
43
44 %***************************************************************************
45 %*                                                                         *
46 \subsection[Socket-Setup]{High Level ``Setup'' functions}
47 %*                                                                         *
48 %***************************************************************************
49
50 Calling @connectTo@ creates a client side socket which is
51 connected to the given host and port.  The Protocol and socket type is
52 derived from the given port identifier.  If a port number is given
53 then the result is always an internet family @Stream@ socket. 
54
55 If the @PortID@ specifies a unix family socket and the @Hostname@
56 differs from that returned by @getHostname@ then an error is
57 raised. Alternatively an empty string may be given to @connectTo@
58 signalling that the current hostname applies.
59
60 \begin{code}
61 data PortID = 
62           Service String                -- Service Name eg "ftp"
63         | PortNumber PortNumber         -- User defined Port Number
64 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
65         | UnixSocket String             -- Unix family socket in file system
66 #endif
67
68 type Hostname = String
69 -- Maybe consider this alternative.
70 -- data Hostname = Name String | IP Int Int Int Int
71 \end{code}
72    
73 If more control over the socket type is required then $socketPrim$
74 should be used instead.
75
76 \begin{code}
77 connectTo :: Hostname           -- Hostname
78           -> PortID             -- Port Identifier
79           -> IO Handle          -- Connected Socket
80
81 connectTo hostname (Service serv) = do
82     proto       <- getProtocolNumber "tcp"
83     sock        <- socket AF_INET Stream proto
84     port        <- getServicePortNumber serv
85     he          <- getHostByName hostname
86     connect sock (SockAddrInet port (hostAddress he))
87     socketToHandle sock ReadWriteMode
88
89 connectTo hostname (PortNumber port) = do
90     proto       <- getProtocolNumber "tcp"
91     sock        <- socket AF_INET Stream proto
92     he          <- getHostByName hostname
93     connect sock (SockAddrInet port (hostAddress he))
94     socketToHandle sock ReadWriteMode
95
96 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
97 connectTo _ (UnixSocket path) = do
98     sock    <- socket AF_UNIX Datagram 0
99     connect sock (SockAddrUnix path)
100     socketToHandle sock ReadWriteMode
101 #endif
102
103 \end{code}
104
105 The dual to the @connectTo@ call. This creates the server side
106 socket which has been bound to the specified port.
107
108 \begin{code}
109 listenOn :: PortID      -- Port Identifier
110          -> IO Socket   -- Connected Socket
111
112 listenOn (Service serv) = do
113     proto   <- getProtocolNumber "tcp"
114     sock    <- socket AF_INET Stream proto
115     port    <- getServicePortNumber serv
116     bindSocket sock (SockAddrInet port iNADDR_ANY)
117     listen sock maxListenQueue
118     return sock
119
120 listenOn (PortNumber port) = do
121     proto <- getProtocolNumber "tcp"
122     sock  <- socket AF_INET Stream proto
123     bindSocket sock (SockAddrInet port iNADDR_ANY)
124     listen sock maxListenQueue
125     return sock
126
127 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
128 listenOn (UnixSocket path) = do
129     sock <- socket AF_UNIX Datagram 0
130     bindSocket sock (SockAddrUnix path)
131     return sock
132 #endif
133 \end{code}
134
135 \begin{code}
136 accept :: Socket                -- Listening Socket
137        -> IO (Handle,           -- StdIO Handle for read/write
138               HostName)         -- HostName of Peer socket
139 accept sock = do
140  ~(sock', (SockAddrInet _ haddr)) <- SocketPrim.accept sock
141  (HostEntry peer _ _ _)           <- getHostByAddr AF_INET haddr
142  handle                           <- socketToHandle sock' ReadWriteMode
143  return (handle, peer)
144
145 \end{code}
146
147 Send and recived data from/to the given host and port number.  These
148 should normally only be used where the socket will not be required for
149 further calls.
150
151 Thse are wrappers around socket, bind, and listen.
152
153 \begin{code}
154 sendTo :: Hostname      -- Hostname
155        -> PortID        -- Port Number
156        -> String        -- Message to send
157        -> IO ()
158 sendTo h p msg = do
159   s <- connectTo h p
160   hPutStr s msg
161   hClose s
162
163 recvFrom :: Hostname    -- Hostname
164          -> PortID      -- Port Number
165          -> IO String   -- Received Data
166 recvFrom host port = do
167  s <- listenOn port
168  let 
169   waiting = do
170      ~(s', SockAddrInet _ haddr) <-  SocketPrim.accept s
171      (HostEntry peer _ _ _)      <- getHostByAddr AF_INET haddr
172      if peer /= host 
173       then do
174          sClose s'
175          waiting
176       else do
177         msg <- readSocketAll s'
178         sClose s'
179         return msg
180
181  message <- waiting
182  sClose s
183  return message
184
185 \end{code}
186
187 Access function returning the port type/id of socket.
188
189 \begin{code}
190 socketPort :: Socket -> IO PortID
191 socketPort s = do
192     sockaddr <- getSocketName s
193     return (portID sockaddr)
194   where
195    portID sa =
196     case sa of
197      SockAddrInet port _    -> PortNumber port
198 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
199      SockAddrUnix path      -> UnixSocket path
200 #endif
201
202 \end{code}