我在SML/NJ
REPL 中运行了一个最小的 TCP 服务器,我想知道如何在键盘中断时优雅地关闭侦听器套接字。服务器的精简版是
fun sendHello sock =
let val res = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello world!\r\n\r\n"
val slc = Word8VectorSlice.full (Byte.stringToBytes res)
in
Socket.sendVec (sock, slc);
Socket.close sock
end
fun acceptLoop serv =
let val (s, _) = Socket.accept serv
in print "Accepted a connection...\n";
sendHello s;
acceptLoop serv
end
fun serve port =
let val s = INetSock.TCP.socket()
in Socket.Ctl.setREUSEADDR (s, true);
Socket.bind(s, INetSock.any port);
Socket.listen(s, 5);
print "Entering accept loop...\n";
acceptLoop s
end
问题是如果我在一个端口上启动这个服务器,用键盘中断取消,然后尝试在同一个端口上重新启动,我得到一个错误。
Standard ML of New Jersey v110.76 [built: Thu Feb 19 00:37:13 2015]
- use "test.sml" ;;
[opening test.sml]
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
val sendHello = fn : ('a,Socket.active Socket.stream) Socket.sock -> unit
val acceptLoop = fn : ('a,Socket.passive Socket.stream) Socket.sock -> 'b
val serve = fn : int -> 'a
val it = () : unit
- serve 8181 ;;
stdIn:2.1-2.11 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
Entering accept loop...
Accepted a connection...
C-c C-c
Interrupt
- serve 8181 ;;
stdIn:1.2-1.12 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
uncaught exception SysErr [SysErr: Address already in use [<UNKNOWN>]]
raised at: <bind.c>
-
所以我希望能够在发生一些错误时关闭监听套接字。Interrupt
当我发出键盘中断时,我在 REPL 中看到,所以我假设这Interrupt
是我希望捕获的异常的构造函数。但是,将适当的handle
行添加到其中一个acceptLoop
或serve
似乎没有做我想要的。
fun acceptLoop serv =
let val (s, _) = Socket.accept serv
in print "Accepted a connection...\n";
sendHello s;
acceptLoop serv
end
handle Interrupt => Socket.close serv
fun serve port =
let val s = INetSock.TCP.socket()
in Socket.Ctl.setREUSEADDR (s, true);
Socket.bind(s, INetSock.any port);
Socket.listen(s, 5);
print "Entering accept loop...\n";
acceptLoop s
handle Interrupt => Socket.close s
end
(然后在 REPL 中)
- use "test.sml" ;;
[opening test.sml]
val sendHello = fn : ('a,Socket.active Socket.stream) Socket.sock -> unit
val acceptLoop = fn : ('a,Socket.passive Socket.stream) Socket.sock -> 'b
val serve = fn : int -> 'a
val it = () : unit
- serve 8182 ;;
stdIn:3.1-3.11 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
Entering accept loop...
Accepted a connection...
C-c C-c
Interrupt
- serve 8182 ;;
stdIn:1.2-1.12 Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
uncaught exception SysErr [SysErr: Address already in use [<UNKNOWN>]]
raised at: <bind.c>
-
对变量 ( handle x => (Socket.close s; raise x)
) 或通配符 ( handle _ => Socket.close s
) 异常匹配执行相同操作具有与上述相同的效果。