]> git.cworth.org Git - ttt/blob - src/ttt-server.c
2005-11-14 Carl Worth <cworth@cworth.org>
[ttt] / src / ttt-server.c
1 /* ttt-server.c - tic-tac-toe game server
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 #include "ttt-args.h"
24 #include "ttt-client.h"
25 #include "ttt-socket.h"
26
27 struct _ttt_server {
28     pthread_mutex_t mutex;
29
30     int next_client_id;
31
32     ttt_client_t **clients;
33     int clients_size;
34     int num_clients;
35 };
36
37 static void
38 ttt_server_init (ttt_server_t *server)
39 {
40     pthread_mutex_init (&server->mutex, NULL);
41
42     server->next_client_id = 0;
43
44     server->clients = NULL;
45     server->clients_size = 0;
46     server->num_clients = 0;
47 }
48
49 static ttt_client_t *
50 ttt_server_create_client (ttt_server_t *server, int client_socket)
51 {
52     ttt_client_t *client;
53
54     pthread_mutex_lock (&server->mutex);
55
56     client = ttt_client_create (server, client_socket,
57                                 server->next_client_id++);
58
59     printf ("Client %d has joined.\n", client->id);
60
61     server->num_clients++;
62
63     if (server->num_clients > server->clients_size) {
64         if (server->clients_size == 0)
65             server->clients_size = 1;
66         else
67             server->clients_size *= 2;
68
69         server->clients = xrealloc (server->clients,
70                                     server->clients_size * sizeof (ttt_client_t *));
71     }
72
73     server->clients [server->num_clients - 1] = client;
74
75     pthread_mutex_unlock (&server->mutex);
76
77     return client;
78 }
79
80 static void
81 ttt_server_destroy_client (ttt_server_t *server, ttt_client_t *client)
82 {
83     int i;
84
85     pthread_mutex_lock (&server->mutex);
86
87     for (i = 0; i < server->num_clients; i++)
88         if (server->clients[i] == client)
89             break;
90
91     assert (i < server->num_clients);
92
93     printf ("Client %d has left.\n", client->id);
94
95     memmove (&server->clients[i], &server->clients[i+1],
96              (server->num_clients - i - 1) * sizeof (ttt_client_t *));
97
98     server->num_clients--;
99
100     ttt_client_destroy (client);
101
102     pthread_mutex_unlock (&server->mutex);
103 }
104
105 static void *
106 _handle_client_requests_thread (void *closure)
107 {
108     ttt_client_t *client = closure;
109     ttt_server_t *server = client->server;
110     char *request;
111
112     while (1) {
113
114         request = ttt_client_read_line (client);
115         if (request == NULL)
116             break;
117
118         ttt_server_broadcast (client->server, request);
119     }
120
121     ttt_server_destroy_client (server, client);
122
123     return (void *) 0;
124 }
125
126 static void
127 _accept_client (void *closure, int client_socket)
128 {
129     ttt_server_t *server = closure;
130     ttt_client_t *client;
131     int err;
132     
133     client = ttt_server_create_client (server, client_socket);
134
135     err = pthread_create (&client->thread, NULL,
136                           _handle_client_requests_thread, client);
137     if (err != 0) {
138         fprintf (stderr, "Error: pthread_create failed: %s. Aborting.\n",
139                  strerror (err));
140         exit (1);
141     }
142 }
143
144 void
145 ttt_server_broadcast (ttt_server_t *server, const char *message)
146 {
147     int i;
148
149     pthread_mutex_lock (&server->mutex);
150
151     for (i = 0; i < server->num_clients; i++)
152         ttt_client_send (server->clients[i], message);
153
154     pthread_mutex_unlock (&server->mutex);
155 }
156
157 static const char *WELCOME_MESSAGE = 
158 "Welcome to ttt-server. So far, this program is still a demonstration\n"
159 "TCP/IP server, acting something like a rather braindead chat server.\n"
160 "The server is currently listening on:\n"
161 "\n     %s:%s\n"
162 "\nTo test this, simply connect one or more clients to that host and port.\n"
163 "For example:\n"
164 "\n     telnet %s %s\n"
165 "\nOnce you have connected a client, the server will send each line of text\n"
166 "it receives to all connected clients. The server reports client joins and\n"
167 "departures on stdout.\n"
168 "\nNote that to terminate the telnet client you type Control-], then\n"
169 "<Enter>, then \"close\" (and <Enter>) at the \"telnet> \" prompt.\n"
170 "\nHave fun!\n"
171 "-Carl\n"
172 "\nPS. At this point we're ready to leave the demonstration phase and to\n"
173 "begin implementing TTTP (tic-tac-toe protocol) as well as fixing the\n"
174 "protocol specifcation. We don't need a custom client to move forward on\n"
175 "the server (that is one of the ideas behind using a telnet-compatible\n"
176 "protocol), but a custom client would still be a fine project for a\n"
177 "motivated beginning programmer.\n\n";
178
179 int 
180 main (int argc, char **argv)
181 {
182     ttt_args_t args;
183     ttt_server_t server;
184     int socket;
185
186     ttt_args_parse (&args, argc, argv);
187
188     if (args.log_file)
189         xfreopen (args.log_file, "a", stderr);
190
191     socket = ttt_socket_create_server (args.host, args.port);
192
193     printf (WELCOME_MESSAGE, args.host, args.port, args.host, args.port);
194
195     ttt_server_init (&server);
196
197     while (1)
198         ttt_socket_accept (socket, _accept_client, &server);
199
200     /* We only reach here if something bad happened. */
201     return 1;
202 }