]> git.cworth.org Git - ttt/blob - src/ttt-server.c
2005-11-09 Carl Worth <cworth@cworth.org>
[ttt] / src / ttt-server.c
1 /* ttt.c - client-server tic-tac-toe game
2  *
3  * Copyright © 2005 Carl Worth
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Author: Carl Worth <cworth@cworth.org>
20  */
21
22 #include "ttt.h"
23
24 static ttt_status_t
25 _sockaddr_init (struct sockaddr_in      *addr,
26                 const char              *host,
27                 const char              *port)
28 {
29     struct hostent *hostent;
30     struct servent *servent;
31     char *endptr;
32     long port_long;
33     uint16_t port_short;
34
35     addr->sin_family = AF_INET;
36
37     hostent = gethostbyname (host);
38     if (hostent == NULL)
39     {
40         fprintf (stderr, "Error: Lookup failed for host %s: %s\n",
41                  host, hstrerror (h_errno));
42         return TTT_STATUS_FAILURE;
43     }
44     memcpy (&addr->sin_addr.s_addr, hostent->h_addr_list[0], 
45             sizeof (addr->sin_addr.s_addr));
46
47     assert (*port != '\0');
48     port_long = strtol (port, &endptr, 10);
49     if (*endptr == '\0')
50     {
51         /* Numeric port*/
52         if (port_long <= 0 || port_long >= (1 << 16)) {
53             fprintf (stderr, "Error: Port %ld out of range.\n", port_long);
54             return TTT_STATUS_FAILURE;
55         }
56         port_short = port_long;
57         addr->sin_port = htons (port_short);
58     } else {
59         /* Named port */
60         servent = getservbyname (port, "tcp");
61         if (port == NULL) {
62             fprintf (stderr, "Error: Lookup failed for port %s: %s\n",
63                      port, hstrerror (h_errno));
64             return TTT_STATUS_FAILURE;
65         }
66         addr->sin_port = servent->s_port;
67     }
68
69     return TTT_STATUS_SUCCESS;
70 }
71
72 static int
73 _wait_for_connection (int listen_socket)
74 {
75     fd_set read_set;
76     int num_selected;
77     int flags, err;
78     int connected_socket;
79
80     FD_ZERO (&read_set);
81     FD_SET (listen_socket, &read_set);
82
83     while (1) {
84         num_selected = select (listen_socket + 1,
85                                &read_set, NULL, NULL,
86                                NULL);
87
88         flags = fcntl (listen_socket, F_GETFL);
89         flags |= O_NONBLOCK;
90         xfcntl (listen_socket, F_SETFL, flags);
91
92         connected_socket = accept (listen_socket, 0, 0);
93         err = errno;
94
95         flags &= ~O_NONBLOCK;
96         xfcntl (listen_socket, F_SETFL, flags);
97
98         if (connected_socket != -1)
99             return connected_socket;
100
101         if (err == EWOULDBLOCK || err == EAGAIN)
102             continue;
103
104         fprintf (stderr, "Error: accept failed: %s. Aborting.\n",
105                  strerror (err));
106         exit (1);
107     }
108 }
109
110 static const char *WELCOME_MESSAGE = 
111 "Welcome to ttt-server. So far, this program is simply a demonstration\n"
112 "of a one-shot TCP/IP server. The server is currently listening on:\n"
113 "\n     %s:%s\n"
114 "\nTo test this program, simply connect a client to that host and port.\n"
115 "For example:\n"
116 "\n     telnet %s %s\n"
117 "\nOnce you have connected a client, the server will echo any characters\n"
118 "it receives back to the client as well as to stdout.\n"
119 "\nNote that to terminate the telnet client you type Control-], then\n"
120 "<Enter>, then \"close\" (and <Enter>) at the \"telnet> \" prompt.\n"
121 "\nHave fun!\n"
122 "-Carl\n"
123 "\nPS. By one-shot, I mean that the server will only accept a connection\n"
124 "from a single client, and the server will exit when that client disconnects\n"
125 "To test the server again, you will need to manually restart it again.\n"
126 "Extending the server to start listening again, or to handle multiple\n"
127 "clients simultaneously would both be fun projects for a motivated\n"
128 "student (as would writing a custom client program).\n\n";
129
130 int 
131 main (int argc, char **argv)
132 {
133     ttt_args_t args;
134     int listen_socket, connected_socket;
135     struct sockaddr_in addr;
136
137     ttt_args_parse (&args, argc, argv);
138
139     if (args.log_file)
140         stderr = xfreopen (args.log_file, "a", stderr);
141
142     listen_socket = xsocket (PF_INET, SOCK_STREAM, 0);
143
144     _sockaddr_init (&addr, args.host, args.port);
145
146 #ifdef SO_REUSEADDR
147     {
148         int one = 1;
149         setsockopt (listen_socket, SOL_SOCKET,
150                     SO_REUSEADDR, (char *) &one, sizeof (int));
151     }
152 #endif
153
154     xbind (listen_socket, (struct sockaddr *) &addr, sizeof (addr));
155
156     xlisten (listen_socket, SOMAXCONN);
157
158     printf (WELCOME_MESSAGE, args.host, args.port, args.host, args.port);
159
160     connected_socket = _wait_for_connection (listen_socket);
161
162 #define BUF_SIZE 1024
163
164     while (1) {
165         char buf[BUF_SIZE];
166         int cnt;
167         cnt = read (connected_socket, buf, BUF_SIZE);
168         if (cnt == 0)
169             break;
170         write (0, buf, cnt);
171         write (connected_socket, buf, cnt);
172     }
173
174     return 0;
175 }