summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormax <max@tclers.tk>2011-06-22 14:32:08 (GMT)
committermax <max@tclers.tk>2011-06-22 14:32:08 (GMT)
commit52975dac483619f642d76da30625db396e4ba62a (patch)
tree274b62d75b972d905d3edaa69f409ea3fbf14d04
parent344567552f325458ea53df5c48079cac131d5aa2 (diff)
parentd63052263676891c816f2dbc51362eaa0e3dc048 (diff)
downloadtcl-52975dac483619f642d76da30625db396e4ba62a.zip
tcl-52975dac483619f642d76da30625db396e4ba62a.tar.gz
tcl-52975dac483619f642d76da30625db396e4ba62a.tar.bz2
Merge fixes for [socket -async] and other improvements to the unix socket code
-rw-r--r--ChangeLog11
-rw-r--r--doc/socket.n23
-rw-r--r--generic/tclIOSock.c7
-rw-r--r--tests/socket.test86
-rw-r--r--unix/tclUnixSock.c365
5 files changed, 321 insertions, 171 deletions
diff --git a/ChangeLog b/ChangeLog
index 8a55dc5..adabfbd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2011-06-22 Reinhard Max <max@suse.de>
+
+ Merge from rmax-ipv6-branch:
+ * unix/tclUnixSock.c: Fix [socket -async], so that all addresses
+ returned by getaddrinfo() are tried, not just the first one. This
+ requires the event loop to be running while the async connection
+ is in progress. ***POTENTIAL INCOMPATIBILITY***
+ * tests/socket.test: Add a test for the above.
+ * doc/socket: Document the fact that -async needs the event loop
+ * generic/tclIOSock.c: AI_ADDRCONFIG is broken on HP-UX
+
2011-06-21 Don Porter <dgp@users.sourceforge.net>
* generic/tclLink.c: Prevent multiple links to a single Tcl
diff --git a/doc/socket.n b/doc/socket.n
index 0e427ed..0cb0595 100644
--- a/doc/socket.n
+++ b/doc/socket.n
@@ -71,12 +71,14 @@ port number will be chosen at random by the system software.
This option will cause the client socket to be connected
asynchronously. This means that the socket will be created immediately
but may not yet be connected to the server, when the call to
-\fBsocket\fR returns. When a \fBgets\fR or \fBflush\fR is done on the
-socket before the connection attempt succeeds or fails, if the socket
-is in blocking mode, the operation will wait until the connection is
-completed or fails. If the socket is in nonblocking mode and a
-\fBgets\fR or \fBflush\fR is done on the socket before the connection
-attempt succeeds or fails, the operation returns immediately and
+\fBsocket\fR returns.
+
+When a \fBgets\fR or \fBflush\fR is done on the socket before the
+connection attempt succeeds or fails, if the socket is in blocking
+mode, the operation will wait until the connection is completed or
+fails. If the socket is in nonblocking mode and a \fBgets\fR or
+\fBflush\fR is done on the socket before the connection attempt
+succeeds or fails, the operation returns immediately and
\fBfblocked\fR on the socket returns 1. Synchronous client sockets may
be switched (after they have connected) to operating in asynchronous
mode using:
@@ -87,6 +89,15 @@ mode using:
.CE
.PP
See the \fBchan\fR \fBconfigure\fR command for more details.
+
+The Tcl event loop should be running while an asynchronous connection
+is in progress, because it may have to do several connection attempts
+in the background. Runnig the event loop also allows you to set up a
+writable channel event on the socket to get notified when the
+asyncronous connection has succeeded or failed. See the \fBvwait\fR
+and the \fBchan\fR comands for more details on the event loop and
+channel events.
+
.RE
.SH "SERVER SOCKETS"
.PP
diff --git a/generic/tclIOSock.c b/generic/tclIOSock.c
index ab2b094..aabd67d 100644
--- a/generic/tclIOSock.c
+++ b/generic/tclIOSock.c
@@ -178,8 +178,11 @@ TclCreateSocketAddress(
}
hints.ai_socktype = SOCK_STREAM;
-#if defined(AI_ADDRCONFIG) && !defined(_AIX)
- /* Missing on: OpenBSD, NetBSD. Causes failure when used on AIX 5.1 */
+#if defined(AI_ADDRCONFIG) && !defined(_AIX) && !defined(__hpux)
+ /*
+ * Missing on: OpenBSD, NetBSD.
+ * Causes failure when used on AIX 5.1 and HP-UX
+ */
hints.ai_flags |= AI_ADDRCONFIG;
#endif
if (willBind) {
diff --git a/tests/socket.test b/tests/socket.test
index 4a9bcb9..7f5c5c2 100644
--- a/tests/socket.test
+++ b/tests/socket.test
@@ -71,6 +71,24 @@ testConstraint exec [llength [info commands exec]]
# from 49152 through 65535.
proc randport {} { expr {int(rand()*16383+49152)} }
+# Test the latency of tcp connections over the loopback interface. Some OSes
+# (e.g. NetBSD) seem to use the Nagle algorithm and delayed ACKs, so it takes
+# up to 200ms for a packet sent to localhost to arrive. We're measuring this
+# here, so that OSes that don't have this problem can run the tests at full
+# speed.
+set server [socket -server {apply {{s a p} {set ::s1 $s}}} 0]
+set s2 [socket localhost [lindex [fconfigure $server -sockname] 2]]
+vwait s1; close $server
+fconfigure $s1 -buffering line
+fconfigure $s2 -buffering line
+set t1 [clock milliseconds]
+puts $s2 test1; gets $s1
+puts $s2 test2; gets $s1
+close $s1; close $s2
+set t2 [clock milliseconds]
+set latency [expr {($t2-$t1)*2}]; # doubled as a safety margin
+unset t1 t2 s1 s2 server
+
# If remoteServerIP or remoteServerPort are not set, check in the environment
# variables for externally set values.
#
@@ -584,7 +602,7 @@ test socket_$af-2.11 {detecting new data} -constraints [list socket supported_$a
fconfigure $sock -blocking 1
puts $s2 two
flush $s2
- after idle {set x 1}
+ after $latency {set x 1}; # NetBSD fails here if we do [after idle]
vwait x
fconfigure $sock -blocking 0
lappend result c:[gets $sock]
@@ -1699,6 +1717,72 @@ if {$remoteProcChan ne ""} {
catch {close $commandSocket}
catch {close $remoteProcChan}
}
+unset ::tcl::unsupported::socketAF
+test socket-14.0 {[socket -async] when server only listens on one address family} \
+ -constraints [list socket supported_any] \
+ -setup {
+ proc accept {s a p} {
+ global x
+ puts $s bye
+ close $s
+ set x ok
+ }
+ set server [socket -server accept -myaddr 127.0.0.1 0]
+ set port [lindex [fconfigure $server -sockname] 2]
+ } -body {
+ set client [socket -async localhost $port]
+ set after [after 1000 {set x [fconfigure $client -error]}]
+ vwait x
+ set x
+ } -cleanup {
+ after cancel $after
+ close $server
+ close $client
+ unset x
+ } -result ok
+test socket-14.1 {[socket -async] fileevent while still connecting} \
+ -constraints [list socket supported_any] \
+ -setup {
+ proc accept {s a p} {
+ global x
+ puts $s bye
+ close $s
+ lappend x ok
+ }
+ set server [socket -server accept -myaddr 127.0.0.1 0]
+ set port [lindex [fconfigure $server -sockname] 2]
+ } -body {
+ set client [socket -async localhost $port]
+ fileevent $client writable {
+ lappend x [expr {[fconfigure $client -error] eq ""}]
+ }
+ set after [after 1000 {set x timeout}]
+ vwait x
+ vwait x
+ set x
+ } -cleanup {
+ after cancel $after
+ close $server
+ close $client
+ unset x
+ } -result {ok 1}
+test socket-14.2 {[socket -async] fileevent connection refused} \
+ -constraints [list socket supported_any] \
+ -body {
+ set client [socket -async localhost [randport]]
+ fileevent $client writable {set x [fconfigure $client -error]}
+ set after [after 1000 {set x timeout}]
+ vwait x
+ if {$x eq "timeout"} {
+ append x ": [fconfigure $client -error]"
+ }
+ set x
+ } -cleanup {
+ after cancel $after
+ close $client
+ unset x
+ } -result "connection refused"
+
::tcltest::cleanupTests
flush stdout
return
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index cb72759..5ace251 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -20,6 +20,10 @@
#define SET_BITS(var, bits) ((var) |= (bits))
#define CLEAR_BITS(var, bits) ((var) &= ~(bits))
+/* "sock" + a pointer in hex + \0 */
+#define SOCK_CHAN_LENGTH 4 + sizeof(void*) * 2 + 1
+#define SOCK_TEMPLATE "sock%lx"
+
/*
* This is needed to comply with the strict aliasing rules of GCC, but it also
* simplifies casting between the different sockaddr types.
@@ -46,12 +50,24 @@ typedef struct TcpFdList {
struct TcpState {
Tcl_Channel channel; /* Channel associated with this file. */
- TcpFdList *fds; /* The file descriptors of the sockets. */
+ TcpFdList fds; /* The file descriptors of the sockets. */
int flags; /* ORed combination of the bitfields defined
* below. */
- Tcl_TcpAcceptProc *acceptProc;
- /* Proc to call on accept. */
- ClientData acceptProcData; /* The data for the accept proc. */
+ /*
+ * Only needed for server sockets
+ */
+ Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
+ ClientData acceptProcData; /* The data for the accept proc. */
+ /*
+ * Only needed for client sockets
+ */
+ struct addrinfo *addrlist; /* addresses to connect to */
+ struct addrinfo *addr; /* iterator over addrlist */
+ struct addrinfo *myaddrlist; /* local address */
+ struct addrinfo *myaddr; /* iterator over myaddrlist */
+ int filehandlers; /* Caches FileHandlers that get set up while
+ * an async socket is not yet connected */
+ int status; /* Cache status of async socket */
};
/*
@@ -89,9 +105,8 @@ struct TcpState {
* Static routines for this file:
*/
-static TcpState * CreateClientSocket(Tcl_Interp *interp, int port,
- const char *host, const char *myaddr,
- int myport, int async);
+static int CreateClientSocket(Tcl_Interp *interp,
+ TcpState *state);
static void TcpAccept(ClientData data, int mask);
static int TcpBlockModeProc(ClientData data, int mode);
static int TcpCloseProc(ClientData instanceData,
@@ -333,7 +348,7 @@ TcpBlockModeProc(
} else {
SET_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
}
- if (TclUnixSetBlockingMode(statePtr->fds->fd, mode) < 0) {
+ if (TclUnixSetBlockingMode(statePtr->fds.fd, mode) < 0) {
return errno;
}
return 0;
@@ -375,7 +390,7 @@ WaitForConnect(
timeOut = -1;
}
errno = 0;
- state = TclUnixWaitForFile(statePtr->fds->fd,
+ state = TclUnixWaitForFile(statePtr->fds.fd,
TCL_WRITABLE | TCL_EXCEPTION, timeOut);
if (state & TCL_EXCEPTION) {
return -1;
@@ -428,7 +443,7 @@ TcpInputProc(
if (WaitForConnect(statePtr, errorCodePtr) != 0) {
return -1;
}
- bytesRead = recv(statePtr->fds->fd, buf, (size_t) bufSize, 0);
+ bytesRead = recv(statePtr->fds.fd, buf, (size_t) bufSize, 0);
if (bytesRead > -1) {
return bytesRead;
}
@@ -478,7 +493,7 @@ TcpOutputProc(
if (WaitForConnect(statePtr, errorCodePtr) != 0) {
return -1;
}
- written = send(statePtr->fds->fd, buf, (size_t) toWrite, 0);
+ written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0);
if (written > -1) {
return written;
}
@@ -522,13 +537,21 @@ TcpCloseProc(
* that called this function, so we do not have to delete them here.
*/
- for (fds = statePtr->fds; fds != NULL; fds = statePtr->fds) {
- statePtr->fds = fds->next;
+ for (fds = &statePtr->fds; fds != NULL; fds = fds->next) {
Tcl_DeleteFileHandler(fds->fd);
if (close(fds->fd) < 0) {
errorCode = errno;
}
- ckfree(fds);
+
+ }
+ for (fds = statePtr->fds.next; fds != NULL; fds = fds->next) {
+ ckfree(fds);
+ }
+ if (statePtr->addrlist != NULL) {
+ freeaddrinfo(statePtr->addrlist);
+ }
+ if (statePtr->myaddrlist != NULL) {
+ freeaddrinfo(statePtr->myaddrlist);
}
ckfree(statePtr);
return errorCode;
@@ -579,7 +602,7 @@ TcpClose2Proc(
}
return TCL_ERROR;
}
- if (shutdown(statePtr->fds->fd,sd) < 0) {
+ if (shutdown(statePtr->fds.fd,sd) < 0) {
errorCode = errno;
}
@@ -632,11 +655,16 @@ TcpGetOptionProc(
socklen_t optlen = sizeof(int);
int err, ret;
- ret = getsockopt(statePtr->fds->fd, SOL_SOCKET, SO_ERROR,
- (char *)&err, &optlen);
- if (ret < 0) {
- err = errno;
- }
+ if (statePtr->status == 0) {
+ ret = getsockopt(statePtr->fds.fd, SOL_SOCKET, SO_ERROR,
+ (char *)&err, &optlen);
+ if (ret < 0) {
+ err = errno;
+ }
+ } else {
+ err = statePtr->status;
+ statePtr->status = 0;
+ }
if (err != 0) {
Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1);
}
@@ -653,7 +681,7 @@ TcpGetOptionProc(
address peername;
socklen_t size = sizeof(peername);
- if (getpeername(statePtr->fds->fd, &peername.sa, &size) >= 0) {
+ if (getpeername(statePtr->fds.fd, &peername.sa, &size) >= 0) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-peername");
Tcl_DStringStartSublist(dsPtr);
@@ -700,7 +728,7 @@ TcpGetOptionProc(
Tcl_DStringAppendElement(dsPtr, "-sockname");
Tcl_DStringStartSublist(dsPtr);
}
- for (fds = statePtr->fds; fds != NULL; fds = fds->next) {
+ for (fds = &statePtr->fds; fds != NULL; fds = fds->next) {
size = sizeof(sockname);
if (getsockname(fds->fd, &(sockname.sa), &size) >= 0) {
int flags = reverseDNS;
@@ -785,16 +813,17 @@ TcpWatchProc(
* TCL_EXCEPTION. */
{
TcpState *statePtr = (TcpState *) instanceData;
- TcpFdList *fds;
-
- for (fds = statePtr->fds; fds != NULL; fds = fds->next) {
- if (mask) {
- Tcl_CreateFileHandler(fds->fd, mask,
- (Tcl_FileProc *) Tcl_NotifyChannel,
- (ClientData) statePtr->channel);
- } else {
- Tcl_DeleteFileHandler(fds->fd);
- }
+
+ if (statePtr->flags & TCP_ASYNC_CONNECT) {
+ /* Async sockets use a FileHandler internally while connecting, so we
+ * need to cache this request until the connection has succeeded. */
+ statePtr->filehandlers = mask;
+ } else if (mask) {
+ Tcl_CreateFileHandler(statePtr->fds.fd, mask,
+ (Tcl_FileProc *) Tcl_NotifyChannel,
+ (ClientData) statePtr->channel);
+ } else {
+ Tcl_DeleteFileHandler(statePtr->fds.fd);
}
}
@@ -825,21 +854,31 @@ TcpGetHandleProc(
{
TcpState *statePtr = (TcpState *) instanceData;
- *handlePtr = INT2PTR(statePtr->fds->fd);
+ *handlePtr = INT2PTR(statePtr->fds.fd);
return TCL_OK;
}
+static void
+TcpAsyncCallback(
+ ClientData clientData, /* The socket state. */
+ int mask) /* Events of interest; an OR-ed combination of
+ * TCL_READABLE, TCL_WRITABLE and
+ * TCL_EXCEPTION. */
+{
+ CreateClientSocket(NULL, clientData);
+}
+
/*
*----------------------------------------------------------------------
*
- * CreateSocket --
+ * CreateClientSocket --
*
- * This function opens a new socket in client or server mode and
- * initializes the TcpState structure.
+ * This function opens a new socket in client mode.
*
* Results:
- * Returns a new TcpState, or NULL with an error in the interp's result,
- * if interp is not NULL.
+ * TCL_OK, if the socket was successfully connected or an asynchronous
+ * connection is in progress. If an error occurs, TCL_ERROR is returned
+ * and an error message is left in interp.
*
* Side effects:
* Opens a socket.
@@ -847,37 +886,27 @@ TcpGetHandleProc(
*----------------------------------------------------------------------
*/
-static TcpState *
+static int
CreateClientSocket(
Tcl_Interp *interp, /* For error reporting; can be NULL. */
- int port, /* Port number to open. */
- const char *host, /* Name of host on which to open port. */
- const char *myaddr, /* Optional client-side address.
- * NULL implies INADDR_ANY/in6addr_any */
- int myport, /* Optional client-side port */
- int async) /* If nonzero and creating a client socket,
- * attempt to do an async connect. Otherwise
- * do a synchronous connect or bind. */
+ TcpState *state)
{
- int status = -1, connected = 0, sock = -1;
- struct addrinfo *addrlist = NULL, *addrPtr;
- /* Socket address */
- struct addrinfo *myaddrlist = NULL, *myaddrPtr;
- /* Socket address for client */
- TcpState *statePtr;
- const char *errorMsg = NULL;
+ socklen_t optlen;
+ int in_coro = (state->addr != NULL);
+ int status;
+ int async = state->flags & TCP_ASYNC_CONNECT;
- if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)) {
- goto error;
- }
- if (!TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1, &errorMsg)) {
- goto error;
+ if (in_coro) {
+ goto coro_continue;
}
+
+ for (state->addr = state->addrlist; state->addr != NULL;
+ state->addr = state->addr->ai_next) {
+
+ status = -1;
- for (addrPtr = addrlist; addrPtr != NULL;
- addrPtr = addrPtr->ai_next) {
- for (myaddrPtr = myaddrlist; myaddrPtr != NULL;
- myaddrPtr = myaddrPtr->ai_next) {
+ for (state->myaddr = state->myaddrlist; state->myaddr != NULL;
+ state->myaddr = state->myaddr->ai_next) {
int reuseaddr;
/*
@@ -885,12 +914,21 @@ CreateClientSocket(
* different families.
*/
- if (myaddrPtr->ai_family != addrPtr->ai_family) {
+ if (state->myaddr->ai_family != state->addr->ai_family) {
continue;
}
- sock = socket(addrPtr->ai_family, SOCK_STREAM, 0);
- if (sock < 0) {
+ /*
+ * Close the socket if it is still open from the last unsuccessful
+ * iteration.
+ */
+ if (state->fds.fd >= 0) {
+ close(state->fds.fd);
+ state->fds.fd = -1;
+ }
+
+ state->fds.fd = socket(state->addr->ai_family, SOCK_STREAM, 0);
+ if (state->fds.fd < 0) {
continue;
}
@@ -899,27 +937,28 @@ CreateClientSocket(
* inherited by child processes.
*/
- fcntl(sock, F_SETFD, FD_CLOEXEC);
+ fcntl(state->fds.fd, F_SETFD, FD_CLOEXEC);
/*
* Set kernel space buffering
*/
- TclSockMinimumBuffers(INT2PTR(sock), SOCKET_BUFSIZE);
+ TclSockMinimumBuffers(INT2PTR(state->fds.fd), SOCKET_BUFSIZE);
if (async) {
- status = TclUnixSetBlockingMode(sock, TCL_MODE_NONBLOCKING);
+ status = TclUnixSetBlockingMode(state->fds.fd, TCL_MODE_NONBLOCKING);
if (status < 0) {
- goto looperror;
+ continue;
}
}
reuseaddr = 1;
- (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (void) setsockopt(state->fds.fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &reuseaddr, sizeof(reuseaddr));
- status = bind(sock, myaddrPtr->ai_addr, myaddrPtr->ai_addrlen);
+ status = bind(state->fds.fd, state->myaddr->ai_addr,
+ state->myaddr->ai_addrlen);
if (status < 0) {
- goto looperror;
+ continue;
}
/*
@@ -929,70 +968,52 @@ CreateClientSocket(
* in being informed when the connect completes.
*/
- status = connect(sock, addrPtr->ai_addr, addrPtr->ai_addrlen);
+ status = connect(state->fds.fd, state->addr->ai_addr,
+ state->addr->ai_addrlen);
if (status < 0 && errno == EINPROGRESS) {
- status = 0;
- }
+ Tcl_CreateFileHandler(state->fds.fd,
+ TCL_WRITABLE | TCL_EXCEPTION,
+ TcpAsyncCallback, state);
+ return TCL_OK;
+
+ coro_continue:
+ Tcl_DeleteFileHandler(state->fds.fd);
+ /*
+ * Read the error state from the socket, to see if the async
+ * connection has succeeded or failed and store the status in
+ * the socket state for later retrieval by [fconfigure -error]
+ */
+ optlen = sizeof(int);
+ getsockopt(state->fds.fd, SOL_SOCKET, SO_ERROR,
+ (char *)&status, &optlen);
+ state->status = status;
+ }
if (status == 0) {
- connected = 1;
- break;
- }
- looperror:
- if (sock != -1) {
- close(sock);
- sock = -1;
+ goto out;
}
}
- if (connected) {
- break;
- }
- status = -1;
- if (sock >= 0) {
- close(sock);
- sock = -1;
- }
}
- if (async) {
- /*
- * Restore blocking mode.
- */
- status = TclUnixSetBlockingMode(sock, TCL_MODE_BLOCKING);
- }
+out:
-error:
- if (addrlist) {
- freeaddrinfo(addrlist);
- }
- if (myaddrlist) {
- freeaddrinfo(myaddrlist);
+ if (async) {
+ CLEAR_BITS(state->flags, TCP_ASYNC_CONNECT);
+ TcpWatchProc(state, state->filehandlers);
+ TclUnixSetBlockingMode(state->fds.fd, TCL_MODE_BLOCKING);
}
-
+
if (status < 0) {
- if (interp != NULL) {
- Tcl_AppendResult(interp, "couldn't open socket: ",
- Tcl_PosixError(interp), NULL);
- if (errorMsg != NULL) {
- Tcl_AppendResult(interp, " (", errorMsg, ")", NULL);
- }
- }
- if (sock != -1) {
- close(sock);
- }
- return NULL;
+ if (in_coro) {
+ Tcl_NotifyChannel(state->channel, TCL_WRITABLE);
+ } else {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "couldn't open socket: ",
+ Tcl_PosixError(interp), NULL);
+ }
+ return TCL_ERROR;
+ }
}
-
- /*
- * Allocate a new TcpState for this socket.
- */
-
- statePtr = ckalloc(sizeof(TcpState));
- statePtr->flags = async ? TCP_ASYNC_CONNECT : 0;
- statePtr->fds = ckalloc(sizeof(TcpFdList));
- memset(statePtr->fds, (int) 0, sizeof(TcpFdList));
- statePtr->fds->fd = sock;
-
- return statePtr;
+ return TCL_OK;
}
/*
@@ -1023,31 +1044,57 @@ Tcl_OpenTcpClient(
* connect. Otherwise we do a blocking
* connect. */
{
- TcpState *statePtr;
- char channelName[16 + TCL_INTEGER_SPACE];
+ TcpState *state;
+ const char *errorMsg = NULL;
+ struct addrinfo *addrlist = NULL, *myaddrlist = NULL;
+ char channelName[SOCK_CHAN_LENGTH];
/*
- * Create a new client socket and wrap it in a channel.
+ * Do the name lookups for the local and remote addresses.
*/
-
- statePtr = CreateClientSocket(interp, port, host, myaddr, myport, async);
- if (statePtr == NULL) {
- return NULL;
+ if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg) ||
+ !TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1, &errorMsg)) {
+ if (addrlist != NULL) {
+ freeaddrinfo(addrlist);
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "couldn't open socket: ",
+ Tcl_PosixError(interp), NULL);
+ if (errorMsg != NULL) {
+ Tcl_AppendResult(interp, " (", errorMsg, ")", NULL);
+ }
+ }
+ return NULL;
}
- statePtr->acceptProc = NULL;
- statePtr->acceptProcData = NULL;
+ /*
+ * Allocate a new TcpState for this socket.
+ */
+ state = ckalloc(sizeof(TcpState));
+ memset(state, 0, sizeof(TcpState));
+ state->flags = async ? TCP_ASYNC_CONNECT : 0;
+ state->addrlist = addrlist;
+ state->myaddrlist = myaddrlist;
+ state->fds.fd = -1;
- sprintf(channelName, "sock%d", statePtr->fds->fd);
+ /*
+ * Create a new client socket and wrap it in a channel.
+ */
+ if (CreateClientSocket(interp, state) != TCL_OK) {
+ TcpCloseProc(state, NULL);
+ return NULL;
+ }
- statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- statePtr, (TCL_READABLE | TCL_WRITABLE));
- if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
+ sprintf(channelName, SOCK_TEMPLATE, (long)state);
+
+ state->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+ state, (TCL_READABLE | TCL_WRITABLE));
+ if (Tcl_SetChannelOption(interp, state->channel, "-translation",
"auto crlf") == TCL_ERROR) {
- Tcl_Close(NULL, statePtr->channel);
+ Tcl_Close(NULL, state->channel);
return NULL;
}
- return statePtr->channel;
+ return state->channel;
}
/*
@@ -1097,17 +1144,14 @@ TclpMakeTcpClientChannelMode(
* TCL_WRITABLE to indicate file mode. */
{
TcpState *statePtr;
- char channelName[16 + TCL_INTEGER_SPACE];
+ char channelName[SOCK_CHAN_LENGTH];
statePtr = ckalloc(sizeof(TcpState));
- statePtr->fds = ckalloc(sizeof(TcpFdList));
- memset(statePtr->fds, (int) 0, sizeof(TcpFdList));
- statePtr->fds->fd = PTR2INT(sock);
+ memset(statePtr, 0, sizeof(TcpState));
+ statePtr->fds.fd = PTR2INT(sock);
statePtr->flags = 0;
- statePtr->acceptProc = NULL;
- statePtr->acceptProcData = NULL;
- sprintf(channelName, "sock%d", statePtr->fds->fd);
+ sprintf(channelName, SOCK_TEMPLATE, (long)statePtr);
statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
statePtr, mode);
@@ -1149,7 +1193,7 @@ Tcl_OpenTcpServer(
int status = 0, sock = -1, reuseaddr = 1, chosenport = 0;
struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */
TcpState *statePtr = NULL;
- char channelName[16 + TCL_INTEGER_SPACE];
+ char channelName[SOCK_CHAN_LENGTH];
const char *errorMsg = NULL;
TcpFdList *fds = NULL, *newfds;
@@ -1230,19 +1274,20 @@ Tcl_OpenTcpServer(
close(sock);
continue;
}
- newfds = ckalloc(sizeof(TcpFdList));
- memset(newfds, (int) 0, sizeof(TcpFdList));
if (statePtr == NULL) {
/*
* Allocate a new TcpState for this socket.
*/
statePtr = ckalloc(sizeof(TcpState));
- statePtr->fds = newfds;
+ memset(statePtr, 0, sizeof(TcpState));
statePtr->acceptProc = acceptProc;
statePtr->acceptProcData = acceptProcData;
- sprintf(channelName, "sock%d", sock);
+ sprintf(channelName, SOCK_TEMPLATE, (long)statePtr);
+ newfds = &statePtr->fds;
} else {
+ newfds = ckalloc(sizeof(TcpFdList));
+ memset(newfds, (int) 0, sizeof(TcpFdList));
fds->next = newfds;
}
newfds->fd = sock;
@@ -1306,7 +1351,7 @@ TcpAccept(
TcpState *newSockState; /* State for new socket. */
address addr; /* The remote address */
socklen_t len; /* For accept interface */
- char channelName[16 + TCL_INTEGER_SPACE];
+ char channelName[SOCK_CHAN_LENGTH];
char host[NI_MAXHOST], port[NI_MAXSERV];
len = sizeof(addr);
@@ -1323,15 +1368,11 @@ TcpAccept(
(void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
newSockState = ckalloc(sizeof(TcpState));
-
+ memset(newSockState, 0, sizeof(TcpState));
newSockState->flags = 0;
- newSockState->fds = ckalloc(sizeof(TcpFdList));
- memset(newSockState->fds, (int) 0, sizeof(TcpFdList));
- newSockState->fds->fd = newsock;
- newSockState->acceptProc = NULL;
- newSockState->acceptProcData = NULL;
+ newSockState->fds.fd = newsock;
- sprintf(channelName, "sock%d", newsock);
+ sprintf(channelName, SOCK_TEMPLATE, (long)newSockState);
newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
newSockState, (TCL_READABLE | TCL_WRITABLE));