diff options
author | Steve Bennett <steveb@workware.net.au> | 2022-08-04 11:13:39 +1000 |
---|---|---|
committer | Steve Bennett <steveb@workware.net.au> | 2022-08-20 15:27:19 +1000 |
commit | 6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254 (patch) | |
tree | 9596172bde8edbcf87bcdb1d9012fa6d0a948167 | |
parent | 5af9d6a919b83e28a37ade2db8090a375a93ba53 (diff) | |
download | jimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.zip jimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.tar.gz jimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.tar.bz2 |
socket: add support for -async
Very similar to Tcl except that read/write can't be done until
writable indicates the socket is connected.
Signed-off-by: Steve Bennett <steveb@workware.net.au>
Documentation fixes -
Co-authored-by: Adrian Ho <the.gromgit@gmail.com>
-rw-r--r-- | jim-aio.c | 71 | ||||
-rw-r--r-- | jim_tcl.txt | 40 |
2 files changed, 87 insertions, 24 deletions
@@ -1209,26 +1209,32 @@ static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } #ifdef O_NDELAY +static int aio_set_nonblocking(int fd, int nb) +{ + int fmode = fcntl(fd, F_GETFL); + if (nb) { + fmode |= O_NDELAY; + } + else { + fmode &= ~O_NDELAY; + } + (void)fcntl(fd, F_SETFL, fmode); + return fmode; +} + static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); - int fmode = fcntl(af->fd, F_GETFL); - if (argc) { long nb; if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) { return JIM_ERR; } - if (nb) { - fmode |= O_NDELAY; - } - else { - fmode &= ~O_NDELAY; - } - (void)fcntl(af->fd, F_SETFL, fmode); + aio_set_nonblocking(af->fd, nb); } + int fmode = fcntl(af->fd, F_GETFL); Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0); return JIM_OK; } @@ -2197,21 +2203,38 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) int type = SOCK_STREAM; Jim_Obj *argv0 = argv[0]; int ipv6 = 0; + int async = 0; - if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) { - if (!IPV6) { - Jim_SetResultString(interp, "ipv6 not supported", -1); + + while (argc > 1 && Jim_String(argv[1])[0] == '-') { + static const char * const options[] = { "-async", "-ipv6", NULL }; + enum { OPT_ASYNC, OPT_IPV6 }; + int option; + + if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } - ipv6 = 1; - family = PF_INET6; + switch (option) { + case OPT_ASYNC: + async = 1; + break; + + case OPT_IPV6: + if (!IPV6) { + Jim_SetResultString(interp, "ipv6 not supported", -1); + return JIM_ERR; + } + ipv6 = 1; + family = PF_INET6; + break; + } + argc--; + argv++; } - argc -= ipv6; - argv += ipv6; if (argc < 2) { wrongargs: - Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?"); + Jim_WrongNumArgs(interp, 1, &argv0, "?-async? ?-ipv6? type ?address?"); return JIM_ERR; } @@ -2347,6 +2370,9 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) JimAioSetError(interp, NULL); return JIM_ERR; } + if (async) { + aio_set_nonblocking(sock, 1); + } if (bind_addr) { if (JimParseSocketAddress(interp, family, type, bind_addr, &sa, &salen) != JIM_OK) { close(sock); @@ -2367,9 +2393,14 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return JIM_ERR; } if (connect(sock, &sa.sa, salen)) { - Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno)); - close(sock); - return JIM_ERR; + if (async && errno == EINPROGRESS) { + /* OK */ + } + else { + Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno)); + close(sock); + return JIM_ERR; + } } } if (do_listen) { diff --git a/jim_tcl.txt b/jim_tcl.txt index f1ab2cd..2738d0b 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -5135,18 +5135,18 @@ Various socket types may be created. +*socket unix.dgram.server* 'path'+:: A unix domain socket datagram server server listening on 'path' -+*socket ?-ipv6? stream* 'addr:port'+:: ++*socket ?-async? ?-ipv6? stream* 'addr:port'+:: A TCP socket client. (See the forms for +'addr'+ below) -+*socket ?-ipv6? stream.server* '?addr:?port'+:: ++*socket ?-async? ?-ipv6? stream.server* '?addr:?port'+:: A TCP socket server (+'addr'+ defaults to +0.0.0.0+ for IPv4 or +[::]+ for IPv6). -+*socket ?-ipv6? dgram* ?'addr:port'?+:: ++*socket ?-async? ?-ipv6? dgram* ?'addr:port'?+:: A UDP socket client. If the address is not specified, the client socket will be unbound and 'sendto' must be used to indicated the destination. -+*socket ?-ipv6? dgram.server* 'addr:port'+:: ++*socket ?-async? ?-ipv6? dgram.server* 'addr:port'+:: A UDP socket server. +*socket pipe*+:: @@ -5209,6 +5209,38 @@ The path for Unix domain sockets is automatically removed when the socket is closed. Use `close -nodelete` in the rare case where this behaviour should be avoided (e.g. after `os.fork`). +If '+-async+' is specified, the socket is opened in non-blocking (ndelay) mode +and 'connect' is non-blocking (applies to 'stream'). In this case, a 'writable' handler +should be used that will fire when the connect succeeds or fails, and calling 'peername' on the handle +will succeed if connected or fail if connect failed. Typical usage is as follows: + +---- + set s [socket -async stream host:port] + + $s writable { + # Remove writable either way + $s writable {} + try { + $s peername + } on error msg { + # Connect failed + incr done + return + } + + $s readable { + set buf [$s read] + if {[$s eof]} { + # Closed connection + incr done + } + ... + } + } + + vwait done +---- + syslog ~~~~~~ +*syslog* '?options? ?priority? message'+ |