Index: ChangeLog ================================================================== --- ChangeLog +++ ChangeLog @@ -1,5 +1,13 @@ +2011-05-27 Reinhard Max + + * 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. + 2011-05-25 Don Porter * library/msgcat/msgcat.tcl: Bump to msgcat 1.4.4. * library/msgcat/pkgIndex.tcl: * unix/Makefile.in Index: doc/socket.n ================================================================== --- doc/socket.n +++ doc/socket.n @@ -69,16 +69,18 @@ \fB\-async\fR . 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: .RS .PP @@ -85,10 +87,19 @@ .CS \fBchan configure \fIchan \fB\-blocking 0\fR .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 If the \fB\-server\fR option is specified then the new socket will be a server that listens on the given \fIport\fR (either an integer or a Index: generic/tclIOSock.c ================================================================== --- generic/tclIOSock.c +++ generic/tclIOSock.c @@ -176,12 +176,15 @@ } } } 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) { hints.ai_flags |= AI_PASSIVE; } Index: tests/socket.test ================================================================== --- tests/socket.test +++ tests/socket.test @@ -68,10 +68,28 @@ testConstraint exec [llength [info commands exec]] # Produce a random port number in the Dynamic/Private range # 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. # @@ -582,11 +600,11 @@ set result a:[gets $sock] lappend result b:[gets $sock] 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] } -cleanup { fconfigure $sock -blocking 1 @@ -1697,13 +1715,79 @@ catch {sendCommand exit} } 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 # Local Variables: # mode: tcl # fill-column: 78 # End: Index: unix/tclUnixSock.c ================================================================== --- unix/tclUnixSock.c +++ unix/tclUnixSock.c @@ -18,10 +18,14 @@ */ #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. */ @@ -44,16 +48,28 @@ struct TcpFdList *next; } 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 */ }; /* * These bits may be ORed together into the "flags" field of a TcpState * structure. @@ -87,13 +103,12 @@ /* * 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, Tcl_Interp *interp); static int TcpClose2Proc(ClientData instanceData, @@ -331,11 +346,11 @@ if (mode == TCL_MODE_BLOCKING) { CLEAR_BITS(statePtr->flags, TCP_ASYNC_SOCKET); } 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; } @@ -373,11 +388,11 @@ timeOut = 0; } else { 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; } if (state & TCL_WRITABLE) { @@ -426,11 +441,11 @@ *errorCodePtr = 0; 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; } if (errno == ECONNRESET) { /* @@ -476,11 +491,11 @@ *errorCodePtr = 0; 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; } *errorCodePtr = errno; return -1; @@ -520,17 +535,25 @@ * part of the mechanism to accept new client connections. Channel * handlers are already deleted in the generic IO channel closing code * 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; } @@ -577,11 +600,11 @@ Tcl_AppendResult(interp, "Socket close2proc called bidirectionally", NULL); } return TCL_ERROR; } - if (shutdown(statePtr->fds->fd,sd) < 0) { + if (shutdown(statePtr->fds.fd,sd) < 0) { errorCode = errno; } return errorCode; } @@ -630,15 +653,20 @@ if ((len > 1) && (optionName[1] == 'e') && (strncmp(optionName, "-error", len) == 0)) { 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); } return TCL_OK; } @@ -651,11 +679,11 @@ ((len > 1) && (optionName[1] == 'p') && (strncmp(optionName, "-peername", len) == 0))) { 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); } @@ -698,11 +726,11 @@ if (len == 0) { 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; found = 1; @@ -783,20 +811,21 @@ int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and * 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); } } /* *---------------------------------------------------------------------- @@ -823,178 +852,170 @@ int direction, /* Not used. */ ClientData *handlePtr) /* Where to store the handle. */ { 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. * *---------------------------------------------------------------------- */ -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. */ -{ - 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; - - if (!TclCreateSocketAddress(interp, &addrlist, host, port, 0, &errorMsg)) { - goto error; - } - if (!TclCreateSocketAddress(interp, &myaddrlist, myaddr, myport, 1, &errorMsg)) { - goto error; - } - - for (addrPtr = addrlist; addrPtr != NULL; - addrPtr = addrPtr->ai_next) { - for (myaddrPtr = myaddrlist; myaddrPtr != NULL; - myaddrPtr = myaddrPtr->ai_next) { + TcpState *state) +{ + socklen_t optlen; + int in_coro = (state->addr != NULL); + int status; + int async = state->flags & TCP_ASYNC_CONNECT; + + if (in_coro) { + goto coro_continue; + } + + for (state->addr = state->addrlist; state->addr != NULL; + state->addr = state->addr->ai_next) { + + status = -1; + + for (state->myaddr = state->myaddrlist; state->myaddr != NULL; + state->myaddr = state->myaddr->ai_next) { int reuseaddr; /* * No need to try combinations of local and remote addresses of * 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; } /* * Set the close-on-exec flag so that the socket will not get * 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; } /* * Attempt to connect. The connect may fail at present with an * EINPROGRESS but at a later time it will complete. The caller * will set up a file handler on the socket if she is interested * in being informed when the connect completes. */ - status = connect(sock, addrPtr->ai_addr, addrPtr->ai_addrlen); - if (status < 0 && errno == EINPROGRESS) { - status = 0; - } - if (status == 0) { - connected = 1; - break; - } - looperror: - if (sock != -1) { - close(sock); - sock = -1; - } - } - if (connected) { - break; - } - status = -1; - if (sock >= 0) { - close(sock); - sock = -1; - } - } - if (async) { - /* - * Restore blocking mode. - */ - - status = TclUnixSetBlockingMode(sock, TCL_MODE_BLOCKING); - } - -error: - if (addrlist) { - freeaddrinfo(addrlist); - } - if (myaddrlist) { - freeaddrinfo(myaddrlist); - } - - 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; - } - - /* - * 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; + status = connect(state->fds.fd, state->addr->ai_addr, + state->addr->ai_addrlen); + if (status < 0 && errno == EINPROGRESS) { + 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) { + goto out; + } + } + } + +out: + + if (async) { + CLEAR_BITS(state->flags, TCP_ASYNC_CONNECT); + TcpWatchProc(state, state->filehandlers); + TclUnixSetBlockingMode(state->fds.fd, TCL_MODE_BLOCKING); + } + + if (status < 0) { + 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; + } + } + return TCL_OK; } /* *---------------------------------------------------------------------- * @@ -1021,35 +1042,61 @@ int myport, /* Client-side port */ int async) /* If nonzero, attempt to do an asynchronous * 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]; + + /* + * Do the name lookups for the local and remote addresses. + */ + 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; + } + + /* + * 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; /* * Create a new client socket and wrap it in a channel. */ - - statePtr = CreateClientSocket(interp, port, host, myaddr, myport, async); - if (statePtr == NULL) { - return NULL; - } - - statePtr->acceptProc = NULL; - statePtr->acceptProcData = NULL; - - sprintf(channelName, "sock%d", statePtr->fds->fd); - - statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, - statePtr, (TCL_READABLE | TCL_WRITABLE)); - if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation", - "auto crlf") == TCL_ERROR) { - Tcl_Close(NULL, statePtr->channel); - return NULL; - } - return statePtr->channel; + if (CreateClientSocket(interp, state) != TCL_OK) { + TcpCloseProc(state, NULL); + return NULL; + } + + 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, state->channel); + return NULL; + } + return state->channel; } /* *---------------------------------------------------------------------- * @@ -1095,21 +1142,18 @@ ClientData sock, /* The socket to wrap up into a channel. */ int mode) /* ORed combination of TCL_READABLE and * 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); if (Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf") == TCL_ERROR) { @@ -1147,11 +1191,11 @@ ClientData acceptProcData) /* Data for the callback. */ { 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; if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1, &errorMsg)) { goto error; @@ -1228,23 +1272,24 @@ status = listen(sock, SOMAXCONN); if (status < 0) { 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; newfds->statePtr = statePtr; fds = newfds; @@ -1304,11 +1349,11 @@ TcpFdList *fds = data; /* Client data of server socket. */ int newsock; /* The new client socket */ 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); newsock = accept(fds->fd, &(addr.sa), &len); if (newsock < 0) { @@ -1321,19 +1366,15 @@ */ (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)); Tcl_SetChannelOption(NULL, newSockState->channel, "-translation", "auto crlf");