]> git.cworth.org Git - ttt/blob - src/ttt-server.c
2005-11-08 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 args_first;
135     int listen_socket, connected_socket;
136     struct sockaddr_in addr;
137
138     ttt_args_parse (&args, argc, argv, &args_first);
139
140     listen_socket = xsocket (PF_INET, SOCK_STREAM, 0);
141
142 #define HOST "localhost"
143 #define PORT "5534"
144
145     _sockaddr_init (&addr, HOST, PORT);
146
147 #ifdef SO_REUSEADDR
148     {
149         int one = 1;
150         setsockopt (listen_socket, SOL_SOCKET,
151                     SO_REUSEADDR, (char *) &one, sizeof (int));
152     }
153 #endif
154
155     xbind (listen_socket, (struct sockaddr *) &addr, sizeof (addr));
156
157     xlisten (listen_socket, SOMAXCONN);
158
159     printf (WELCOME_MESSAGE, HOST, PORT, HOST, PORT);
160
161     connected_socket = _wait_for_connection (listen_socket);
162
163 #define BUF_SIZE 1024
164
165     while (1) {
166         char buf[BUF_SIZE];
167         int cnt;
168         cnt = read (connected_socket, buf, BUF_SIZE);
169         if (cnt == 0)
170             break;
171         write (0, buf, cnt);
172         write (connected_socket, buf, cnt);
173     }
174
175     return 0;
176 }