summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/socket.n13
-rw-r--r--tests/ioCmd.test4
-rw-r--r--unix/tclUnixSock.c133
-rw-r--r--win/tclWinSock.c80
4 files changed, 169 insertions, 61 deletions
diff --git a/doc/socket.n b/doc/socket.n
index 8836150..b7b3228 100644
--- a/doc/socket.n
+++ b/doc/socket.n
@@ -162,7 +162,8 @@ described below.
.SH "CONFIGURATION OPTIONS"
.PP
The \fBchan configure\fR command can be used to query several readonly
-configuration options for socket channels:
+configuration options for socket channels or in some cases to set
+alternative properties on socket channels:
.TP
\fB\-error\fR
.
@@ -204,6 +205,16 @@ list is identical to the address, its first element.
\fB\-connecting\fR
.
This option is not supported by server sockets. For client sockets, this option returns 1 if an asyncroneous connect is still in progress, 0 otherwise.
+.TP
+\fB\-keepalive\fR
+.
+This option sets or queries the TCP keepalive option on the socket as 1 if
+keepalive is turned on, 0 otherwise.
+.TP
+\fB\-nodelay\fR
+.
+This option sets or queries the TCP nodelay option on the socket as 1 if
+nodelay is turned on, 0 otherwise.
.PP
.SH "EXAMPLES"
.PP
diff --git a/tests/ioCmd.test b/tests/ioCmd.test
index 409d4ec..876da8b 100644
--- a/tests/ioCmd.test
+++ b/tests/ioCmd.test
@@ -294,7 +294,7 @@ removeFile fconfigure.dummy
test iocmd-8.14 {fconfigure command} {
fconfigure stdin -buffers
} 4096
-test iocmd-8.15.1 {fconfigure command / tcp channel} -constraints {socket unixOrWin} -setup {
+test iocmd-8.15 {fconfigure command / tcp channel} -constraints {socket unixOrWin} -setup {
set srv [socket -server iocmdSRV -myaddr 127.0.0.1 0]
set port [lindex [fconfigure $srv -sockname] 2]
proc iocmdSRV {sock ip port} {close $sock}
@@ -306,7 +306,7 @@ test iocmd-8.15.1 {fconfigure command / tcp channel} -constraints {socket unixOr
close $srv
unset cli srv port
rename iocmdSRV {}
-} -returnCodes error -result [expectedOpts "-blah" {-connecting -peername -sockname}]
+} -returnCodes error -result [expectedOpts "-blah" {-connecting -keepalive -nodelay -peername -sockname}]
test iocmd-8.16 {fconfigure command / tcp channel} -constraints socket -setup {
set srv [socket -server iocmdSRV -myaddr 127.0.0.1 0]
set port [lindex [fconfigure $srv -sockname] 2]
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index da26e52..af1782d 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -9,6 +9,7 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
+#include <netinet/tcp.h>
#include "tclInt.h"
/*
@@ -139,6 +140,9 @@ static int TcpInputProc(void *instanceData, char *buf,
int toRead, int *errorCode);
static int TcpOutputProc(void *instanceData,
const char *buf, int toWrite, int *errorCode);
+static int TcpSetOptionProc(void *instanceData,
+ Tcl_Interp *interp, const char *optionName,
+ const char *value);
static void TcpThreadActionProc(void *instanceData, int action);
static void TcpWatchProc(void *instanceData, int mask);
static int WaitForConnect(TcpState *statePtr, int *errorCodePtr);
@@ -156,7 +160,7 @@ static const Tcl_ChannelType tcpChannelType = {
TcpInputProc, /* Input proc. */
TcpOutputProc, /* Output proc. */
NULL, /* Seek proc. */
- NULL, /* Set option proc. */
+ TcpSetOptionProc, /* Set option proc. */
TcpGetOptionProc, /* Get option proc. */
TcpWatchProc, /* Initialize notifier. */
TcpGetHandleProc, /* Get OS handles out of channel. */
@@ -805,6 +809,87 @@ TcpHostPortList(
/*
*----------------------------------------------------------------------
*
+ * TcpSetOptionProc --
+ *
+ * Sets TCP channel specific options.
+ *
+ * Results:
+ * None, unless an error happens.
+ *
+ * Side effects:
+ * Changes attributes of the socket at the system level.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TcpSetOptionProc(
+ void *instanceData, /* Socket state. */
+ Tcl_Interp *interp, /* For error reporting - can be NULL. */
+ const char *optionName, /* Name of the option to set. */
+ const char *value) /* New value for option. */
+{
+ TcpState *statePtr = (TcpState *)instanceData;
+ size_t len = 0;
+
+ if (optionName != NULL) {
+ len = strlen(optionName);
+ }
+
+ if ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0)) {
+ int val = 0, ret;
+
+ if (Tcl_GetBoolean(interp, value, &val) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#if defined(SO_KEEPALIVE)
+ ret = setsockopt(statePtr->fds.fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *) &val, sizeof(int));
+#else
+ ret = -1;
+ Tcl_SetErrno(ENOTSUP);
+#endif
+ if (ret < 0) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+ if ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nodelay", len) == 0)) {
+ int val = 0, ret;
+
+ if (Tcl_GetBoolean(interp, value, &val) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#if defined(SOL_TCP) && defined(TCP_NODELAY)
+ ret = setsockopt(statePtr->fds.fd, SOL_TCP, TCP_NODELAY,
+ (const char *) &val, sizeof(int));
+#else
+ ret = -1;
+ Tcl_SetErrno(ENOTSUP);
+#endif
+ if (ret < 0) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+ return Tcl_BadChannelOption(interp, optionName, "keepalive nodelay");
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TcpGetOptionProc --
*
* Computes an option value for a TCP socket based channel, or a list of
@@ -825,7 +910,7 @@ TcpHostPortList(
static int
TcpGetOptionProc(
- void *instanceData, /* Socket state. */
+ void *instanceData, /* Socket state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Name of the option to retrieve the value
* for, or NULL to get all options and their
@@ -836,8 +921,6 @@ TcpGetOptionProc(
TcpState *statePtr = (TcpState *)instanceData;
size_t len = 0;
- WaitForConnect(statePtr, NULL);
-
if (optionName != NULL) {
len = strlen(optionName);
}
@@ -846,6 +929,7 @@ TcpGetOptionProc(
(strncmp(optionName, "-error", len) == 0)) {
socklen_t optlen = sizeof(int);
+ WaitForConnect(statePtr, NULL);
if (GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) {
/*
* Suppress errors as long as we are not done.
@@ -870,6 +954,7 @@ TcpGetOptionProc(
if ((len > 1) && (optionName[1] == 'c') &&
(strncmp(optionName, "-connecting", len) == 0)) {
+ WaitForConnect(statePtr, NULL);
Tcl_DStringAppend(dsPtr,
GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", TCL_INDEX_NONE);
return TCL_OK;
@@ -880,6 +965,7 @@ TcpGetOptionProc(
address peername;
socklen_t size = sizeof(peername);
+ WaitForConnect(statePtr, NULL);
if (GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) {
/*
* In async connect output an empty string
@@ -931,6 +1017,7 @@ TcpGetOptionProc(
socklen_t size;
int found = 0;
+ WaitForConnect(statePtr, NULL);
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-sockname");
Tcl_DStringStartSublist(dsPtr);
@@ -964,9 +1051,45 @@ TcpGetOptionProc(
}
}
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0))) {
+ socklen_t size;
+ int opt = 0;
+
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-keepalive");
+ }
+#if defined(SO_KEEPALIVE)
+ getsockopt(statePtr->fds.fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &opt, &size);
+#endif
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
+ if (len > 0) {
+ return TCL_OK;
+ }
+ }
+
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nodelay", len) == 0))) {
+ socklen_t size;
+ int opt = 0;
+
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-nodelay");
+ }
+#if defined(SOL_TCP) && defined(TCP_NODELAY)
+ getsockopt(statePtr->fds.fd, SOL_TCP, TCP_NODELAY,
+ (char *) &opt, &size);
+#endif
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
+ if (len > 0) {
+ return TCL_OK;
+ }
+ }
+
if (len > 0) {
return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname");
+ "connecting keepalive nodelay peername sockname");
}
return TCL_OK;
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index 660c8d1..2174210 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -55,13 +55,6 @@
#endif
/*
- * Support for control over sockets' KEEPALIVE and NODELAY behavior is
- * currently disabled.
- */
-
-#undef TCL_FEATURE_KEEPALIVE_NAGLE
-
-/*
* Helper macros to make parts of this file clearer. The macros do exactly
* what they say on the tin. :-) They also only ever refer to their arguments
* once, and so can be used without regard to side effects.
@@ -1174,14 +1167,15 @@ TcpSetOptionProc(
void *instanceData, /* Socket state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Name of the option to set. */
- TCL_UNUSED(const char *) /*value*/) /* New value for option. */
+ const char *value) /* New value for option. */
{
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- TcpState *statePtr = instanceData;
+ TcpState *statePtr = (TcpState *)instanceData;
SOCKET sock;
-#else
- (void)instanceData;
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ size_t len = 0;
+
+ if (optionName != NULL) {
+ len = strlen(optionName);
+ }
/*
* Check that WinSock is initialized; do not call it if not, to prevent
@@ -1197,20 +1191,17 @@ TcpSetOptionProc(
return TCL_ERROR;
}
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-#error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list"
sock = statePtr->sockets->fd;
- if (!strcasecmp(optionName, "-keepalive")) {
- BOOL val = FALSE;
+ if ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0)) {
+ BOOL val;
int boolVar, rtn;
if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
return TCL_ERROR;
}
- if (boolVar) {
- val = TRUE;
- }
+ val = boolVar ? TRUE : FALSE;
rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
(const char *) &val, sizeof(BOOL));
if (rtn != 0) {
@@ -1223,16 +1214,16 @@ TcpSetOptionProc(
return TCL_ERROR;
}
return TCL_OK;
- } else if (!strcasecmp(optionName, "-nagle")) {
- BOOL val = FALSE;
+ }
+ if ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nodelay", len) == 0)) {
+ BOOL val;
int boolVar, rtn;
if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
return TCL_ERROR;
}
- if (!boolVar) {
- val = TRUE;
- }
+ val = boolVar ? TRUE : FALSE;
rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(const char *) &val, sizeof(BOOL));
if (rtn != 0) {
@@ -1246,11 +1237,7 @@ TcpSetOptionProc(
}
return TCL_OK;
}
-
- return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
-#else
- return Tcl_BadChannelOption(interp, optionName, "");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ return Tcl_BadChannelOption(interp, optionName, "keepalive nodelay");
}
/*
@@ -1524,54 +1511,43 @@ TcpGetOptionProc(
}
}
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0))) {
int optlen;
BOOL opt = FALSE;
if (len == 0) {
+ sock = statePtr->sockets->fd;
Tcl_DStringAppendElement(dsPtr, "-keepalive");
}
optlen = sizeof(BOOL);
getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "1");
- } else {
- Tcl_DStringAppendElement(dsPtr, "0");
- }
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
if (len > 0) {
return TCL_OK;
}
}
- if (len == 0 || !strncmp(optionName, "-nagle", len)) {
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nodelay", len) == 0))) {
int optlen;
BOOL opt = FALSE;
if (len == 0) {
- Tcl_DStringAppendElement(dsPtr, "-nagle");
+ sock = statePtr->sockets->fd;
+ Tcl_DStringAppendElement(dsPtr, "-nodelay");
}
optlen = sizeof(BOOL);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "0");
- } else {
- Tcl_DStringAppendElement(dsPtr, "1");
- }
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
if (len > 0) {
return TCL_OK;
}
}
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
if (len > 0) {
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname keepalive nagle");
-#else
return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ "connecting keepalive nodelay peername sockname");
}
return TCL_OK;
@@ -1660,8 +1636,6 @@ TcpGetHandleProc(
*handlePtr = INT2PTR(statePtr->sockets->fd);
return TCL_OK;
}
-
-
/*
*----------------------------------------------------------------------