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