Attachment "patch.002" to
ticket [0b9d3ba2ba]
added by
cklein
2016-11-18 16:36:35.
Index: generic/tclIOCmd.c
==================================================================
--- generic/tclIOCmd.c
+++ generic/tclIOCmd.c
@@ -1483,16 +1483,16 @@
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
static const char *const socketOptions[] = {
- "-async", "-myaddr", "-myport", "-server", NULL
+ "-async", "-myaddr", "-myport", "-server", "-reuseport", NULL
};
enum socketOptions {
- SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
+ SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT
};
- int optionIndex, a, server = 0, port, myport = 0, async = 0;
+ int optionIndex, a, server = 0, port, myport = 0, async = 0, reuseport = 0;
const char *host, *myaddr = NULL;
Tcl_Obj *script = NULL;
Tcl_Channel chan;
if (TclpHasSockets(interp) != TCL_OK) {
@@ -1555,10 +1555,13 @@
"no argument given for -server option", -1));
return TCL_ERROR;
}
script = objv[a];
break;
+ case SKT_REUSEPORT:
+ reuseport = 1;
+ break;
default:
Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
}
}
if (server) {
@@ -1598,10 +1601,16 @@
ckalloc(sizeof(AcceptCallback));
Tcl_IncrRefCount(script);
acceptCallbackPtr->script = script;
acceptCallbackPtr->interp = interp;
+
+ /* Hint for Tcl_OpenTcpServer to set socket option REUSEPORT */
+ if(reuseport) {
+ port |= (1 << 16);
+ }
+
chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
acceptCallbackPtr);
if (chan == NULL) {
Tcl_DecrRefCount(script);
ckfree(acceptCallbackPtr);
Index: unix/tclUnixSock.c
==================================================================
--- unix/tclUnixSock.c
+++ unix/tclUnixSock.c
@@ -111,10 +111,15 @@
* a socket.
*/
#define SOCKET_BUFSIZE 4096
+#ifdef SO_REUSEPORT
+/* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */
+#define USE_SOCK_REUSEPORT (1 << 16)
+#endif
+
/*
* Static routines for this file:
*/
static int TcpConnect(Tcl_Interp *interp,
@@ -1433,10 +1438,14 @@
struct addrinfo *addrlist = NULL, *addrPtr; /* socket address */
TcpState *statePtr = NULL;
char channelName[SOCK_CHAN_LENGTH];
const char *errorMsg = NULL;
TcpFdList *fds = NULL, *newfds;
+#ifdef SO_REUSEPORT
+ int reuseport = port & USE_SOCK_REUSEPORT;
+ CLEAR_BITS(port, USE_SOCK_REUSEPORT);
+#endif
/*
* Try to record and return the most meaningful error message, i.e. the
* one from the first socket that went the farthest before it failed.
*/
@@ -1509,10 +1518,21 @@
* specified port.
*/
(void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *) &reuseaddr, sizeof(reuseaddr));
+
+#ifdef SO_REUSEPORT
+ /*
+ * Set up to allows multiple sockets on the same host to bind to the same port.
+ * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff).
+ */
+ if(reuseport) {
+ (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+ (char *) &reuseport, sizeof(reuseport));
+ }
+#endif
/*
* Make sure we use the same port number when opening two server
* sockets for IPv4 and IPv6 on a random port.
*
Index: win/tclWinSock.c
==================================================================
--- win/tclWinSock.c
+++ win/tclWinSock.c
@@ -2109,12 +2109,12 @@
htons(chosenport);
}
/*
* Bind to the specified port. Note that we must not call
- * setsockopt with SO_REUSEADDR because Microsoft allows addresses
- * to be reused even if they are still in use.
+ * setsockopt with SO_REUSEADDR or SO_REUSEPORT because Microsoft
+ * allows addresses and ports to be reused even if they are still in use.
*
* Bind should not be affected by the socket having already been
* set into nonblocking mode. If there is trouble, this is one
* place to look for bugs.
*/