Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Annotation of /smlnj-lib/trunk/INet/sock-util.sml
ViewVC logotype

Annotation of /smlnj-lib/trunk/INet/sock-util.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6155 - (view) (download)

1 : jhr 6155 (* sock-util.sml
2 : monnier 409 *
3 : jhr 6155 * COPYRIGHT (c) 2020 The Fellowship of SML/NJ (http://www.smlnj.org)
4 :     * All rights reserved.
5 : monnier 409 *
6 :     * Various utility functions for programming with sockets.
7 :     *)
8 :    
9 :     structure SockUtil : SOCK_UTIL =
10 :     struct
11 :    
12 :     structure C = Char
13 :     structure PC = ParserComb
14 :    
15 :     datatype port = PortNumber of int | ServName of string
16 :     (* a port can be identified by number, or by the name of a service *)
17 :    
18 :     datatype hostname = HostName of string | HostAddr of NetHostDB.in_addr
19 :    
20 :     (** This belongs in an Option structure **)
21 :     fun filterPartial pred NONE = NONE
22 :     | filterPartial pred (SOME x) = if (pred x) then SOME x else NONE
23 :    
24 :     fun scanName getc strm = let
25 :     fun isNameChr (#".", _) = true
26 :     | isNameChr (#"-", _) = true
27 :     | isNameChr (c, _) = C.isAlphaNum c
28 :     fun getName (strm, cl) = (case filterPartial isNameChr (getc strm)
29 :     of SOME(c, strm') => getName(strm', c::cl)
30 :     | NONE => SOME(implode(rev cl), strm)
31 :     (* end case *))
32 :     in
33 :     case (filterPartial (C.isAlpha o #1) (getc strm))
34 :     of SOME(c, strm) => getName(strm, [c])
35 :     | NONE => NONE
36 :     (* end case *)
37 :     end
38 :    
39 :     (* scan an address, which has the form
40 :     * addr [ ":" port ]
41 :     * where the addr may either be numeric or symbolic host name and the
42 :     * port is either a service name or a decimal number. Legal host names
43 :     * must begin with a letter, and may contain any alphanumeric character,
44 :     * the minus sign (-) and period (.), where the period is used as a
45 : jhr 6155 * domain separator.
46 : monnier 409 *)
47 :     fun scanAddr getc strm =
48 :     PC.seqWith (fn (host, port) => {host=host, port=port}) (
49 :     PC.or (
50 :     PC.wrap (scanName, HostName),
51 :     PC.wrap (NetHostDB.scan, HostAddr)),
52 :     PC.option (
53 :     PC.seqWith #2 (
54 :     PC.eatChar (fn c => (c = #":")),
55 :     PC.or (
56 :     PC.wrap (scanName, ServName),
57 :     PC.wrap (Int.scan StringCvt.DEC, PortNumber))))) getc strm
58 :    
59 : jhr 967 val addrFromString = StringCvt.scanString scanAddr
60 :    
61 : monnier 409 exception BadAddr of string
62 :    
63 :     fun resolveAddr {host, port} = let
64 :     fun err (a, b) = raise BadAddr(concat[a, " \"", b, "\" not found"])
65 :     val (name, addr) = (case host
66 :     of HostName s => (case NetHostDB.getByName s
67 :     of NONE => err ("hostname", s)
68 :     | (SOME entry) => (s, NetHostDB.addr entry)
69 :     (* end case *))
70 :     | HostAddr addr => (case NetHostDB.getByAddr addr
71 :     of NONE => err ("host address", NetHostDB.toString addr)
72 :     | (SOME entry) => (NetHostDB.name entry, addr)
73 :     (* end case *))
74 :     (* end case *))
75 :     val port = (case port
76 :     of (SOME(PortNumber n)) => SOME n
77 :     | (SOME(ServName s)) => (case NetServDB.getByName(s, NONE)
78 :     of (SOME entry) => SOME(NetServDB.port entry)
79 :     | NONE => err("service", s)
80 :     (* end case *))
81 :     | NONE => NONE
82 :     (* end case *))
83 :     in
84 :     {host = name, addr = addr, port = port}
85 :     end
86 :    
87 :     type 'a stream_sock = ('a, Socket.active Socket.stream) Socket.sock
88 :    
89 :     (* establish a client-side connection to a INET domain stream socket *)
90 :     fun connectINetStrm {addr, port} = let
91 :     val sock = INetSock.TCP.socket ()
92 :     in
93 :     Socket.connect (sock, INetSock.toAddr(addr, port));
94 :     sock
95 :     end
96 :    
97 :     (** If the server closes the connection, do we get 0 bytes or an error??? **)
98 :     (* read exactly n bytes from a stream socket *)
99 :     fun recvVec (sock, n) = let
100 :     fun get (0, data) = Word8Vector.concat(rev data)
101 :     | get (n, data) = let
102 :     val v = Socket.recvVec (sock, n)
103 :     in
104 :     if (Word8Vector.length v = 0)
105 :     then raise OS.SysErr("closed socket", NONE)
106 :     else get (n - Word8Vector.length v, v::data)
107 :     end
108 :     in
109 :     if (n < 0) then raise Size else get (n, [])
110 :     end
111 :    
112 :     fun recvStr arg = Byte.bytesToString (recvVec arg)
113 :    
114 :     (* send the complete contents of a vector *)
115 :     fun sendVec (sock, vec) = let
116 :     val len = Word8Vector.length vec
117 : jhr 3318 fun send i = Socket.sendVec (sock, Word8VectorSlice.slice (vec, i, NONE))
118 : monnier 409 fun put i = if (i < len)
119 :     then put(i + send i)
120 :     else ()
121 :     in
122 :     put 0
123 :     end
124 :    
125 :     fun sendStr (sock, str) = sendVec (sock, Byte.stringToBytes str)
126 :    
127 :     (* send the complete contents of an array *)
128 :     fun sendArr (sock, arr) = let
129 :     val len = Word8Array.length arr
130 : jhr 3318 fun send i = Socket.sendArr (sock, Word8ArraySlice.slice (arr, i, NONE))
131 : monnier 409 fun put i = if (i < len)
132 :     then put(i + send i)
133 :     else ()
134 :     in
135 :     put 0
136 :     end
137 :    
138 :     end;

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0