]> git.cworth.org Git - ttt/commitdiff
2005-11-09 Carl Worth <cworth@cworth.org>
authorCarl Worth <carl@theworths.org>
Wed, 9 Nov 2005 14:37:16 +0000 (14:37 +0000)
committerCarl Worth <carl@theworths.org>
Wed, 9 Nov 2005 14:37:16 +0000 (14:37 +0000)
        * src/Makefile.am:
        * src/ttt.h:
        * src/ttt-server.c: (ttt_dispatch), (main):
        * src/ttt-socket.h:
        * src/ttt-socket.c: (_sockaddr_init), (_wait_for_connection),
        (ttt_socket_create_server), (ttt_socket_accept): Push a lot of
        code out of ttt-server.c down into a new ttt_socket abstraction
        layer. The code is much easier to read now. The server also no
        longer exits when the first client exits, but continues to listen
        for more connections.

ChangeLog
src/Makefile.am
src/ttt-server.c
src/ttt-socket.c [new file with mode: 0644]
src/ttt-socket.h [new file with mode: 0644]
src/ttt.h

index ec9cc3f958cb7396ec9e0e18673ab9e1b103b8b1..3ff9e6bf600c9431c83944c4c6d4d9c4249b0c8a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-11-09  Carl Worth  <cworth@cworth.org>
+
+       * src/Makefile.am:
+       * src/ttt.h:
+       * src/ttt-server.c: (ttt_dispatch), (main):
+       * src/ttt-socket.h:
+       * src/ttt-socket.c: (_sockaddr_init), (_wait_for_connection),
+       (ttt_socket_create_server), (ttt_socket_accept): Push a lot of
+       code out of ttt-server.c down into a new ttt_socket abstraction
+       layer. The code is much easier to read now. The server also no
+       longer exits when the first client exits, but continues to listen
+       for more connections.
+
 2005-11-09  Carl Worth  <cworth@cworth.org>
 
        * src/ttt-args.h:
index 4c3bd16aaa4e8100b2f6baa6a457e8a46bcfd87e..792b1ecc7ce42e5b1803e1eb24df180b8c0f405f 100644 (file)
@@ -5,6 +5,8 @@ ttt_common_sources =            \
        ttt-args.h              \
        ttt-board.c             \
        ttt-board.h             \
+       ttt-socket.c            \
+       ttt-socket.h            \
        x.c                     \
        x.h
 
index d10d442e621f4b024c9e5cc31f6e8ed4f10d77ce..2cac1177209caf16d7cafb6b2394e384490352e0 100644 (file)
  */
 
 #include "ttt.h"
+#include "ttt-socket.h"
 
-static ttt_status_t
-_sockaddr_init (struct sockaddr_in     *addr,
-               const char              *host,
-               const char              *port)
+static void
+ttt_dispatch (int connected_socket)
 {
-    struct hostent *hostent;
-    struct servent *servent;
-    char *endptr;
-    long port_long;
-    uint16_t port_short;
-
-    addr->sin_family = AF_INET;
-
-    hostent = gethostbyname (host);
-    if (hostent == NULL)
-    {
-       fprintf (stderr, "Error: Lookup failed for host %s: %s\n",
-                host, hstrerror (h_errno));
-       return TTT_STATUS_FAILURE;
-    }
-    memcpy (&addr->sin_addr.s_addr, hostent->h_addr_list[0], 
-           sizeof (addr->sin_addr.s_addr));
-
-    assert (*port != '\0');
-    port_long = strtol (port, &endptr, 10);
-    if (*endptr == '\0')
-    {
-       /* Numeric port*/
-       if (port_long <= 0 || port_long >= (1 << 16)) {
-           fprintf (stderr, "Error: Port %ld out of range.\n", port_long);
-           return TTT_STATUS_FAILURE;
-       }
-       port_short = port_long;
-       addr->sin_port = htons (port_short);
-    } else {
-       /* Named port */
-       servent = getservbyname (port, "tcp");
-       if (port == NULL) {
-           fprintf (stderr, "Error: Lookup failed for port %s: %s\n",
-                    port, hstrerror (h_errno));
-           return TTT_STATUS_FAILURE;
-       }
-       addr->sin_port = servent->s_port;
-    }
-
-    return TTT_STATUS_SUCCESS;
-}
-
-static int
-_wait_for_connection (int listen_socket)
-{
-    fd_set read_set;
-    int num_selected;
-    int flags, err;
-    int connected_socket;
-
-    FD_ZERO (&read_set);
-    FD_SET (listen_socket, &read_set);
+#define BUF_SIZE 1024
 
     while (1) {
-       num_selected = select (listen_socket + 1,
-                              &read_set, NULL, NULL,
-                              NULL);
-
-       flags = fcntl (listen_socket, F_GETFL);
-       flags |= O_NONBLOCK;
-       xfcntl (listen_socket, F_SETFL, flags);
-
-       connected_socket = accept (listen_socket, 0, 0);
-       err = errno;
-
-       flags &= ~O_NONBLOCK;
-       xfcntl (listen_socket, F_SETFL, flags);
-
-       if (connected_socket != -1)
-           return connected_socket;
-
-       if (err == EWOULDBLOCK || err == EAGAIN)
-           continue;
-
-       fprintf (stderr, "Error: accept failed: %s. Aborting.\n",
-                strerror (err));
-       exit (1);
+       char buf[BUF_SIZE];
+       int cnt;
+       cnt = read (connected_socket, buf, BUF_SIZE);
+       if (cnt == 0)
+           break;
+       write (0, buf, cnt);
+       write (connected_socket, buf, cnt);
     }
 }
 
 static const char *WELCOME_MESSAGE = 
 "Welcome to ttt-server. So far, this program is simply a demonstration\n"
-"of a one-shot TCP/IP server. The server is currently listening on:\n"
+"of a TCP/IP server that handles one client at a time. The server is\n"
+"currently listening on:\n"
 "\n    %s:%s\n"
 "\nTo test this program, simply connect a client to that host and port.\n"
 "For example:\n"
@@ -120,56 +52,30 @@ static const char *WELCOME_MESSAGE =
 "<Enter>, then \"close\" (and <Enter>) at the \"telnet> \" prompt.\n"
 "\nHave fun!\n"
 "-Carl\n"
-"\nPS. By one-shot, I mean that the server will only accept a connection\n"
-"from a single client, and the server will exit when that client disconnects\n"
-"To test the server again, you will need to manually restart it again.\n"
-"Extending the server to start listening again, or to handle multiple\n"
-"clients simultaneously would both be fun projects for a motivated\n"
-"student (as would writing a custom client program).\n\n";
+"\nPS. The server handles only one client at a time, but multiple clients\n"
+"may be able to connect simultaneously. Subsequent clients will see no\n"
+"output from the server until all previous clients exit.\n"
+"\nExtending the server to fork a new process for each client would be a fun\n"
+"project for a motivated student (as would writing a custom client program).\n\n";
 
 int 
 main (int argc, char **argv)
 {
     ttt_args_t args;
-    int listen_socket, connected_socket;
-    struct sockaddr_in addr;
+    int socket;
 
     ttt_args_parse (&args, argc, argv);
 
     if (args.log_file)
        stderr = xfreopen (args.log_file, "a", stderr);
 
-    listen_socket = xsocket (PF_INET, SOCK_STREAM, 0);
-
-    _sockaddr_init (&addr, args.host, args.port);
-
-#ifdef SO_REUSEADDR
-    {
-       int one = 1;
-       setsockopt (listen_socket, SOL_SOCKET,
-                   SO_REUSEADDR, (char *) &one, sizeof (int));
-    }
-#endif
-
-    xbind (listen_socket, (struct sockaddr *) &addr, sizeof (addr));
-
-    xlisten (listen_socket, SOMAXCONN);
+    socket = ttt_socket_create_server (args.host, args.port);
 
     printf (WELCOME_MESSAGE, args.host, args.port, args.host, args.port);
 
-    connected_socket = _wait_for_connection (listen_socket);
-
-#define BUF_SIZE 1024
-
-    while (1) {
-       char buf[BUF_SIZE];
-       int cnt;
-       cnt = read (connected_socket, buf, BUF_SIZE);
-       if (cnt == 0)
-           break;
-       write (0, buf, cnt);
-       write (connected_socket, buf, cnt);
-    }
+    while (1)
+       ttt_socket_accept (socket, ttt_dispatch);
 
-    return 0;
+    /* We only reach here if something bad happened. */
+    return 1;
 }
diff --git a/src/ttt-socket.c b/src/ttt-socket.c
new file mode 100644 (file)
index 0000000..765cab1
--- /dev/null
@@ -0,0 +1,156 @@
+/* ttt-socket.c - Simple interface for creating sockets.
+ *
+ * Copyright © 2005 Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "ttt-socket.h"
+
+#include <netinet/ip.h>
+#include <netdb.h>
+
+/* Initialize a TCP/IP address structure with the given host and port.
+ *
+ * Lookup for host (e.g. /etc/hosts and DNA) and port (e.g /etc/services)
+ * will be performed if necessary.
+ *
+ * A special host value of "0.0.0.0" may be used to prepare a server
+ * socket to bind to all available addresses.
+ *
+ * Errors: If any lookup error occurs, this function will not return.
+ */
+static void
+_sockaddr_init (struct sockaddr_in     *addr,
+               const char              *host,
+               const char              *port)
+{
+    struct hostent *hostent;
+    struct servent *servent;
+    char *endptr;
+    long port_long;
+    uint16_t port_short;
+
+    addr->sin_family = AF_INET;
+
+    hostent = gethostbyname (host);
+    if (hostent == NULL)
+    {
+       fprintf (stderr, "Error: Lookup failed for host %s: %s\n",
+                host, hstrerror (h_errno));
+       exit (1);
+    }
+    memcpy (&addr->sin_addr.s_addr, hostent->h_addr_list[0], 
+           sizeof (addr->sin_addr.s_addr));
+
+    assert (*port != '\0');
+    port_long = strtol (port, &endptr, 10);
+    if (*endptr == '\0')
+    {
+       /* Numeric port*/
+       if (port_long <= 0 || port_long >= (1 << 16)) {
+           fprintf (stderr, "Error: Port %ld out of range.\n", port_long);
+           exit (1);
+       }
+       port_short = port_long;
+       addr->sin_port = htons (port_short);
+    } else {
+       /* Named port */
+       servent = getservbyname (port, "tcp");
+       if (port == NULL) {
+           fprintf (stderr, "Error: Lookup failed for port %s: %s\n",
+                    port, hstrerror (h_errno));
+           exit (1);
+       }
+       addr->sin_port = servent->s_port;
+    }
+}
+
+static int
+_wait_for_connection (int listen_socket)
+{
+    fd_set read_set;
+    int num_selected;
+    int flags, err;
+    int connected_socket;
+
+    FD_ZERO (&read_set);
+    FD_SET (listen_socket, &read_set);
+
+    while (1) {
+       num_selected = select (listen_socket + 1,
+                              &read_set, NULL, NULL,
+                              NULL);
+
+       flags = fcntl (listen_socket, F_GETFL);
+       flags |= O_NONBLOCK;
+       xfcntl (listen_socket, F_SETFL, flags);
+
+       connected_socket = accept (listen_socket, 0, 0);
+       err = errno;
+
+       flags &= ~O_NONBLOCK;
+       xfcntl (listen_socket, F_SETFL, flags);
+
+       if (connected_socket != -1)
+           return connected_socket;
+
+       if (err == EWOULDBLOCK || err == EAGAIN)
+           continue;
+
+       fprintf (stderr, "Error: accept failed: %s. Aborting.\n",
+                strerror (err));
+       exit (1);
+    }
+}
+
+/* Exported: see ttt-socket.h for documentation. */
+int
+ttt_socket_create_server (const char *host, const char *port)
+{
+    int listen_socket;
+    struct sockaddr_in addr;
+
+    listen_socket = xsocket (PF_INET, SOCK_STREAM, 0);
+
+    _sockaddr_init (&addr, host, port);
+
+#ifdef SO_REUSEADDR
+    {
+       int one = 1;
+       setsockopt (listen_socket, SOL_SOCKET,
+                   SO_REUSEADDR, (char *) &one, sizeof (int));
+    }
+#endif
+
+    xbind (listen_socket, (struct sockaddr *) &addr, sizeof (addr));
+
+    xlisten (listen_socket, SOMAXCONN);
+
+    return listen_socket;
+}
+
+/* Exported: see ttt-socket.h for documentation. */
+void
+ttt_socket_accept (int listen_socket, ttt_socket_dispatch_func_t dispatch)
+{
+    int connected_socket;
+
+    connected_socket = _wait_for_connection (listen_socket);
+
+    (dispatch) (connected_socket);
+}
diff --git a/src/ttt-socket.h b/src/ttt-socket.h
new file mode 100644 (file)
index 0000000..59e89a5
--- /dev/null
@@ -0,0 +1,54 @@
+/* ttt-socket.c - Simple interface for creating sockets.
+ *
+ * Copyright © 2005 Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef _TTT_SOCKET_H_
+#define _TTT_SOCKET_H_
+
+#include "ttt.h"
+
+typedef void (*ttt_socket_dispatch_func_t) (int connected_socket);
+
+/* Create a socket, bind it to the given host and port, and listen in
+ * preparation for incoming connections. See ttt_socket_accept for a
+ * way to actually accept those incoming connections.
+ *
+ * Lookup for host (e.g. /etc/hosts and DNA) and port (e.g /etc/services)
+ * will be performed if necessary.
+ *
+ * A special host value of "0.0.0.0" may be used to prepare a server
+ * socket to bind to all available addresses.
+ *
+ * Return value: The created socket as a file descriptor.
+ *
+ * Errors: If any error occurs, this function will not return.
+ */
+int
+ttt_socket_create_server (const char *host, const char *port);
+
+/* Wait for an incoming connection on listen_socket, (which should be
+ * a valid socket on which bind and listen have already been
+ * called---see ttt_socket_create_server), then call the dispatch
+ * function with the new socket from the connection.
+ */
+void
+ttt_socket_accept (int listen_socket, ttt_socket_dispatch_func_t dispatch);
+
+#endif
index 42b8c433c4c353431d0c3a3b4a74d9c7255d03b4..0820212d68f699dd33a82f4f4a9293175ca2fd01 100644 (file)
--- a/src/ttt.h
+++ b/src/ttt.h
@@ -39,8 +39,6 @@
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <netinet/ip.h>
-#include <netdb.h>
 
 #define ASSERT_NOT_REACHED             \
 do {                                   \