[gnio] Make reuse address a bind argument instead of a setting



commit b50eaecbae7a03b0a73494d7e70054f1a08a093b
Author: Alexander Larsson <alexl redhat com>
Date:   Thu May 14 14:11:36 2009 +0200

    Make reuse address a bind argument instead of a setting
    
    Since we can't always turn this on, but its almost always what
    you want for a server socket we make this an argument to bind.
    That way all users of bind (i.e. all server sockets and a few
    other users) need to decide on this and can set it.
---
 gio/gsocket.c       |  166 ++++++++++++++-------------------------------------
 gio/gsocket.h       |    1 +
 gio/gtcplistener.c  |    4 +-
 gio/gunixlistener.c |    2 +-
 test/server.c       |    5 +-
 5 files changed, 50 insertions(+), 128 deletions(-)

diff --git a/gio/gsocket.c b/gio/gsocket.c
index 1cdb67b..80e112a 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -118,7 +118,6 @@ enum
   PROP_FD,
   PROP_BLOCKING,
   PROP_LISTEN_BACKLOG,
-  PROP_REUSE_ADDRESS,
   PROP_KEEPALIVE,
   PROP_LOCAL_ADDRESS,
   PROP_REMOTE_ADDRESS
@@ -136,7 +135,6 @@ struct _GSocketPrivate
   GSocketAddress *remote_address;
   guint           inited : 1;
   guint           blocking : 1;
-  guint           reuse_address : 1;
   guint           keepalive : 1;
   guint           closed : 1;
 #ifdef G_OS_WIN32
@@ -395,19 +393,6 @@ g_socket_details_from_fd (GSocket *socket)
       socket->priv->keepalive = FALSE;
     }
 
-  optlen = sizeof bool_val;
-  if (getsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
-		  (void *)&bool_val, &optlen) == 0)
-    {
-      g_assert (optlen == sizeof bool_val);
-      socket->priv->reuse_address = !!bool_val;
-    }
-  else
-    {
-      /* Can't read, maybe not supported, assume FALSE */
-      socket->priv->reuse_address = FALSE;
-    }
-
   return;
 
  err:
@@ -561,10 +546,6 @@ g_socket_get_property (GObject    *object,
         g_value_set_int (value, socket->priv->listen_backlog);
         break;
 
-      case PROP_REUSE_ADDRESS:
-        g_value_set_boolean (value, socket->priv->reuse_address);
-        break;
-
       case PROP_KEEPALIVE:
         g_value_set_boolean (value, socket->priv->keepalive);
         break;
@@ -616,10 +597,6 @@ g_socket_set_property (GObject      *object,
 	g_socket_set_listen_backlog (socket, g_value_get_int (value));
         break;
 
-      case PROP_REUSE_ADDRESS:
-        g_socket_set_reuse_address (socket, g_value_get_boolean (value));
-        break;
-
       case PROP_KEEPALIVE:
         g_socket_set_keepalive (socket, g_value_get_boolean (value));
         break;
@@ -736,13 +713,6 @@ g_socket_class_init (GSocketClass *klass)
                                                      10,
                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-  g_object_class_install_property (gobject_class, PROP_REUSE_ADDRESS,
-                                   g_param_spec_boolean ("reuse-address",
-                                                         P_("Reuse address"),
-                                                         P_("Allow reuse of local addresses when binding"),
-                                                         FALSE,
-                                                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
   g_object_class_install_property (gobject_class, PROP_KEEPALIVE,
                                    g_param_spec_boolean ("keepalive",
                                                          P_("Keep connection alive"),
@@ -779,7 +749,6 @@ g_socket_init (GSocket *socket)
   socket->priv->fd = -1;
   socket->priv->blocking = TRUE;
   socket->priv->listen_backlog = 10;
-  socket->priv->reuse_address = FALSE;
   socket->priv->construct_error = NULL;
   socket->priv->remote_address = NULL;
   socket->priv->local_address = NULL;
@@ -928,80 +897,11 @@ g_socket_get_blocking (GSocket *socket)
 }
 
 /**
- * g_socket_set_reuse_address:
- * @socket: a #GSocket.
- * @reuse: Whether to use allow address reuse or not.
- *
- * Setting @reuse to %TRUE enables reusing the socket
- * address used by a previous socket when binding to
- * an address with g_socket_bind().
- *
- * The exact behaviour of this call varies a bit. For TCP sockets on Unix
- * it means that we allow reuse of a socket address that was previously in
- * use and still have outstanding connections in the WAIT state. Enabling
- * this for a server socket is generally safe and useful, as it means you
- * can quickly restart a server application without waiting for all the
- * old connections to time out.
- *
- * However, on windows it means that all reuse of the address is allowed,
- * even if there is another actively listening socket bound to that port.
- * This is not generally a good thing to use. However, on Windows you
- * are automatically allowed to bind to an address if there are outstanding
- * connections in the WAIT state, so address reuse is not as important
- * there as on Unix.
- *
- * Since: 2.22
- **/
-void
-g_socket_set_reuse_address (GSocket  *socket,
-                            gboolean  reuse)
-{
-  int value;
-
-  g_return_if_fail (G_IS_SOCKET (socket));
-
-  reuse = !!reuse;
-  if (socket->priv->reuse_address == reuse)
-    return;
-
-  value = (int) reuse;
-  if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR,
-		  (gpointer) &value, sizeof (value)) < 0)
-    {
-      int errsv = get_socket_errno ();
-      g_warning ("error setting reuse address: %s", socket_strerror (errsv));
-      return;
-    }
-
-  socket->priv->reuse_address = reuse;
-  g_object_notify (G_OBJECT (socket), "reuse_address");
-}
-
-/**
- * g_socket_get_reuse_address:
- * @socket: a #GSocket.
- *
- * Gets the reuse_address mode of the socket. For details on this,
- * see g_socket_set_reuse_address().
- *
- * Returns: %TRUE if address reuse is used, %FALSE otherwise.
- *
- * Since: 2.22
- **/
-gboolean
-g_socket_get_reuse_address (GSocket *socket)
-{
-  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
-
-  return socket->priv->reuse_address;
-}
-
-/**
  * g_socket_set_keepalive:
  * @socket: a #GSocket.
  * @keepalive: Whether to use try to keep the connection alive or not.
  *
- * Setting @reuse to %TRUE enables the sending of periodic ping requests
+ * Setting @keepalive to %TRUE enables the sending of periodic ping requests
  * on idle connections in order to keep the connection alive. This is only
  * useful for connection oriented sockets. The exact period used between
  * each ping is system and protocol dependent.
@@ -1100,7 +1000,7 @@ g_socket_set_listen_backlog (GSocket *socket,
   if (backlog != socket->priv->listen_backlog)
     {
       socket->priv->listen_backlog = backlog;
-      g_object_notify (G_OBJECT (socket), "reuse_address");
+      g_object_notify (G_OBJECT (socket), "listen-backlog");
     }
 }
 
@@ -1312,6 +1212,7 @@ g_socket_listen (GSocket  *socket,
  * g_socket_bind:
  * @socket: a #GSocket.
  * @address: a #GSocketAddress specifying the local address.
+ * @allow_reuse: whether to allow reusing this address
  * @error: #GError for error reporting, or %NULL to ignore.
  *
  * When a socket is created it is attached to an address family, but it
@@ -1321,40 +1222,65 @@ g_socket_listen (GSocket  *socket,
  * It is generally required to bind to a local address before you can
  * receive connections. (See g_socket_listen() and g_socket_accept() ).
  *
+ * If @allow_reuse is %TRUE this allows the bind call to succeed in some
+ * situation where it would otherwise return a %G_IO_ERROR_ADDRESS_IN_USE
+ * error. The main example is for a TCP server socket where there are
+ * outstanding connections in the WAIT state, which are generally safe
+ * to ignore. However, setting it to %TRUE doesn't mean the call will
+ * succeed if there is a socket actively bound to the address.
+ *
+ * In general, pass %TRUE if the socket will be used to accept connections,
+ * otherwise pass %FALSE.
+ *
  * Returns: %TRUE on success, %FALSE on error.
  *
  * Since: 2.22
  **/
 gboolean
 g_socket_bind (GSocket         *socket,
-               GSocketAddress  *address,
-               GError         **error)
+	       GSocketAddress  *address,
+	       gboolean         reuse_address,
+	       GError         **error)
 {
+  gchar addr[256];
+  int value;
+
   g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
 
   if (!check_socket (socket, error))
     return FALSE;
 
-  {
-    gchar addr[256];
-
-    if (!g_socket_address_to_native (address, addr, sizeof addr))
+  /* SO_REUSEADDR on windows means something else and is not what we want.
+     It always allows the unix variant of SO_REUSEADDR anyway */
+#ifndef G_OS_WIN32
+  value = (int) !!reuse_address;
+  if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR,
+		  (gpointer) &value, sizeof (value)) < 0)
+    {
+      int errsv = get_socket_errno ();
+      g_set_error (error,
+		   G_IO_ERROR, socket_io_error_from_errno (errsv),
+		   _("Error setting reuse_address: %s"), socket_strerror (errsv));
       return FALSE;
+    }
+#endif
 
-    if (bind (socket->priv->fd, (struct sockaddr *) addr,
-	      g_socket_address_get_native_size (address)) < 0)
-      {
-	int errsv = get_socket_errno ();
-	g_set_error (error,
-		     G_IO_ERROR, socket_io_error_from_errno (errsv),
-		     _("Error binding to address: %s"), socket_strerror (errsv));
-        return FALSE;
-      }
+  if (!g_socket_address_to_native (address, addr, sizeof addr))
+    return FALSE;
 
-    socket->priv->local_address = g_object_ref (address);
+  if (bind (socket->priv->fd, (struct sockaddr *) addr,
+	    g_socket_address_get_native_size (address)) < 0)
+    {
+      int errsv = get_socket_errno ();
+      g_set_error (error,
+		   G_IO_ERROR, socket_io_error_from_errno (errsv),
+		   _("Error binding to address: %s"), socket_strerror (errsv));
+      return FALSE;
+    }
 
-    return TRUE;
-  }
+  socket->priv->local_address = g_object_ref (address);
+
+  return TRUE;
 }
 
 /**
diff --git a/gio/gsocket.h b/gio/gsocket.h
index 4cf0aa8..5984b74 100644
--- a/gio/gsocket.h
+++ b/gio/gsocket.h
@@ -187,6 +187,7 @@ void                   g_socket_set_listen_backlog      (GSocket
 gboolean               g_socket_is_connected            (GSocket                 *socket);
 gboolean               g_socket_bind                    (GSocket                 *socket,
 							 GSocketAddress          *address,
+							 gboolean                 allow_reuse,
 							 GError                 **error);
 gboolean               g_socket_connect                 (GSocket                 *socket,
 							 GSocketAddress          *address,
diff --git a/gio/gtcplistener.c b/gio/gtcplistener.c
index 02a2c1a..60eb52a 100644
--- a/gio/gtcplistener.c
+++ b/gio/gtcplistener.c
@@ -86,11 +86,9 @@ g_tcp_listener_socket_factory (GSocketListener  *listener,
   if (socket == NULL)
     return NULL;
 
-  g_socket_set_reuse_address (socket, TRUE);
-
   sockaddr = g_inet_socket_address_new (address, tcp_listener->priv->port);
 
-  if (!g_socket_bind (socket, sockaddr, error))
+  if (!g_socket_bind (socket, sockaddr, TRUE, error))
     {
       g_object_unref (socket);
       socket = NULL;
diff --git a/gio/gunixlistener.c b/gio/gunixlistener.c
index 4cba4ca..95ab516 100644
--- a/gio/gunixlistener.c
+++ b/gio/gunixlistener.c
@@ -54,7 +54,7 @@ g_unix_listener_socket_factory (GSocketListener  *listener,
   sockaddr = g_unix_socket_address_new (unix_listener->priv->path);
 
   /* XXX fix */
-  g_socket_bind (socket, sockaddr, NULL);
+  g_socket_bind (socket, sockaddr, TRUE, NULL);
   g_socket_listen (socket, NULL);
 
   g_object_unref (sockaddr);
diff --git a/test/server.c b/test/server.c
index c598539..0733af5 100644
--- a/test/server.c
+++ b/test/server.c
@@ -151,14 +151,11 @@ main (int argc,
       return 1;
     }
 
-  if (!dont_reuse_address)
-    g_socket_set_reuse_address (socket, TRUE);
-
   if (non_blocking)
     g_socket_set_blocking (socket, FALSE);
 
   src_address = g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), port);
-  if (!g_socket_bind (socket, src_address, &error))
+  if (!g_socket_bind (socket, src_address, !dont_reuse_address, &error))
     {
       g_printerr ("Can't bind socket: %s\n", error->message);
       return 1;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]