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