From 2cd70db8433cc9d02a4ca784190260889c835198 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 8 Nov 2005 18:03:08 +0000 Subject: [PATCH] 2005-11-08 Carl Worth * src/ttt.h: * src/ttt-server.c: (_sockaddr_init), (_wait_for_connection), (main): Add socket, bind, listen, accept calls to provide a simple, functional, one-shot server demonstration. * src/x.h: * src/x.c: (xsocket), (xbind), (xlisten), (xfcntl), (xselect): Add more wrapped system calls. --- ChangeLog | 11 ++++ src/ttt-server.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++- src/ttt.h | 13 +++++ src/x.c | 75 +++++++++++++++++++++++++ src/x.h | 19 +++++++ 5 files changed, 260 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 08b40e2..b9e4563 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2005-11-08 Carl Worth + + * src/ttt.h: + * src/ttt-server.c: (_sockaddr_init), (_wait_for_connection), + (main): Add socket, bind, listen, accept calls to provide a + simple, functional, one-shot server demonstration. + + * src/x.h: + * src/x.c: (xsocket), (xbind), (xlisten), (xfcntl), (xselect): Add + more wrapped system calls. + 2005-11-07 Carl Worth * src/ttt.h: Grab some useful definitions from wdo.h: diff --git a/src/ttt-server.c b/src/ttt-server.c index b42db7a..a248fa1 100644 --- a/src/ttt-server.c +++ b/src/ttt-server.c @@ -21,15 +21,156 @@ #include "ttt.h" +static ttt_status_t +_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)); + 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); + + 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); + } +} + +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" +"\n %s:%s\n" +"\nTo test this program, simply connect a client to that host and port.\n" +"For example:\n" +"\n telnet %s %s\n" +"\nOnce you have connected a client, the server will echo any characters\n" +"it receives back to the client as well as to stdout.\n" +"\nNote that to terminate the telnet client you type Control-], then\n" +", then \"close\" (and ) 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"; + int main (int argc, char **argv) { ttt_args_t args; int args_first; + int listen_socket, connected_socket; + struct sockaddr_in addr; ttt_args_parse (&args, argc, argv, &args_first); - /* XXX: insert code here */ + listen_socket = xsocket (PF_INET, SOCK_STREAM, 0); + +#define HOST "localhost" +#define PORT "5534" + + _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); + + printf (WELCOME_MESSAGE, HOST, PORT, HOST, 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); + } return 0; } diff --git a/src/ttt.h b/src/ttt.h index 0f2f7e5..42b8c43 100644 --- a/src/ttt.h +++ b/src/ttt.h @@ -30,11 +30,17 @@ #define _GNU_SOURCE #include #include +#include #include +#include #include #include #include #include +#include +#include +#include +#include #define ASSERT_NOT_REACHED \ do { \ @@ -53,4 +59,11 @@ do { \ #include "ttt-args.h" #include "x.h" +typedef int ttt_bool_t; + +typedef enum { + TTT_STATUS_SUCCESS = 0, + TTT_STATUS_FAILURE +} ttt_status_t; + #endif diff --git a/src/x.c b/src/x.c index 4062294..1695374 100644 --- a/src/x.c +++ b/src/x.c @@ -161,3 +161,78 @@ xfwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream) exit (1); } } + +int +xsocket (int domain, int type, int protocol) +{ + int ret; + + ret = socket (domain, type, protocol); + if (ret == -1) { + fprintf (stderr, "Error: socket failed: %s. Aborting.\n", + strerror (errno)); + exit (1); + } + + return ret; +} + +void +xbind (int sockfd, const struct sockaddr *my_addr, socklen_t addrlen) +{ + int ret; + + ret = bind (sockfd, my_addr, addrlen); + if (ret == -1) { + fprintf (stderr, "Error: bind failed: %s. Aborting.\n", + strerror (errno)); + exit (1); + } +} + +void +xlisten (int s, int backlog) +{ + int ret; + + ret = listen (s, backlog); + if (ret == -1) { + fprintf (stderr, "Error: listen failed: %s. Aborting.\n", + strerror (errno)); + exit (1); + } +} + +int +xfcntl (int fd, int cmd, long arg) +{ + int ret; + + ret = fcntl (fd, cmd, arg); + if (ret == -1) { + fprintf (stderr, "Error: fcntl failed: %s. Aborting.\n", + strerror (errno)); + exit (1); + } + + return ret; +} + +int +xselect (int n, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout) +{ + int ret; + + ret = select (n, readfds, writefds, exceptfds, timeout); + if (ret == -1) { + fprintf (stderr, "Error: select failed: %s. Aborting.\n", + strerror (errno)); + exit (1); + } + + return ret; +} diff --git a/src/x.h b/src/x.h index 45bf6a9..e3efdb8 100644 --- a/src/x.h +++ b/src/x.h @@ -52,4 +52,23 @@ xstrdup (const char *s); void xfwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream); +int +xsocket (int domain, int type, int protocol); + +void +xbind (int sockfd, const struct sockaddr *my_addr, socklen_t addrlen); + +void +xlisten (int s, int backlog); + +int +xfcntl (int fd, int cmd, long arg); + +int +xselect (int n, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout); + #endif /* _X_H_ */ -- 2.43.0