001 /*
002 * Created on Jul 19, 2004
003 *
004 * To change the template for this generated file go to
005 * Window>Preferences>Java>Code Generation>Code and Comments
006 */
007 package fang;
008
009 import java.io.*;
010 import java.net.*;
011 import java.util.*;
012
013
014 /**
015 * This establishes incoming connections and
016 * directs them to the appropriate GameConnection.
017 * The Server can be run continually on a dedicated
018 * computer to serve all multiclient games.
019 * @author Jam Jenkins
020 */
021 public class HttpServer extends Thread
022 {
023 /**
024 * port on which to run the server
025 */
026 public static int PORT = 1554;
027
028 /**
029 * map of currently running games to the GameConnections
030 */
031 private HashMap<String, GameConnections> games =
032 new HashMap<String, GameConnections>();
033
034 /**
035 * true indicates still accepting new connections,
036 * false indicates accepting no more connections
037 */
038 private boolean connected = true;
039
040 /**
041 * the output stream used for applets
042 */
043 private ObjectOutputStream pipedOut;
044
045 /**
046 * the input stream used for applets
047 */
048 private ObjectInputStream pipedIn;
049
050 /**
051 * the socket listening for new connections
052 */
053 private ServerSocket serverSocket;
054
055 /**the constructor to use when using pipes*/
056 public HttpServer(Object dummy)
057 {}
058
059 /**the constructor to use in applications
060 * @throws IOException */
061 public HttpServer() throws IOException
062 {
063 serverSocket = new HttpServerSocket(PORT);
064 }
065
066 /**makes a piped input stream to use in applets
067 * @return the piped input stream connected to the server
068 */
069 public ObjectInputStream getPipedInput()
070 {
071 try
072 {
073 PipedOutputStream out = new PipedOutputStream();
074 PipedInputStream in = new PipedInputStream();
075 out.connect(in);
076 pipedOut = new ObjectOutputStream(out);
077 return new ObjectInputStream(in);
078 }
079 catch (IOException ioe)
080 {
081 ioe.printStackTrace();
082 pipedOut = null;
083 return null;
084 }
085 }
086
087 /**makes a piped output stream to use in applets
088 * @return the piped output stream connected to the server
089 */
090 public ObjectOutputStream getPipedOutput()
091 {
092 try
093 {
094 PipedInputStream in = new PipedInputStream();
095 PipedOutputStream out = new PipedOutputStream();
096 in.connect(out);
097 ObjectOutputStream toReturn = new ObjectOutputStream(out);
098 pipedIn = new ObjectInputStream(in);
099 return toReturn;
100 }
101 catch (IOException ioe)
102 {
103 ioe.printStackTrace();
104 pipedIn = null;
105 return null;
106 }
107 }
108
109 /**
110 * starts pipe communications
111 */
112 public void startPipes()
113 {
114 if (pipedOut != null && pipedIn != null)
115 {
116 new Dispatcher(pipedOut, pipedIn).start();
117 }
118 else
119 System.err.println("cannot start pipes");
120 // seems like a bug in the JDK, but this
121 // thread cannot end, or the Pipe breaks.
122 synchronized (this)
123 {
124 try
125 {
126 wait();
127 }
128 catch (Exception e)
129 {
130 e.printStackTrace();
131 }
132 }
133 }
134
135 /**
136 * connects to a client and forwards the connections
137 * to the proper GameConnection
138 *
139 * @throws Exception
140 * if an error occurs while establishing the initial connections
141 */
142 public void connect()
143 {
144
145 while (connected)
146 {
147 Socket socket = null;
148 try
149 {
150 //java.lang.System.out.println("accepting connection");
151 socket = serverSocket.accept();
152 //java.lang.System.out.println("accepted connection");
153 }
154 catch (IOException ioe)
155 {
156 ioe.printStackTrace();
157 break;
158 }
159 try
160 {
161 //java.lang.System.out.println("server making out");
162 OutputStream oStream = socket.getOutputStream();
163 oStream.write("?PNG".getBytes());
164 oStream.write(13);
165 oStream.write(10);
166 oStream.write(26);
167 oStream.write(10);
168 oStream.flush();
169 ObjectOutputStream out = new ObjectOutputStream(oStream);
170 //out.writeObject("hello\n");
171 out.flush();
172 //java.lang.System.out.println("server making in");
173 ObjectInputStream in = new ObjectInputStream(
174 new BufferedInputStream(socket.getInputStream(), 100));
175 //java.lang.System.out.println("server dispatching");
176 new Dispatcher(out, in).start();
177 }
178 catch (IOException ioe)
179 {
180 ioe.printStackTrace();
181 }
182 }
183 }
184
185 /**
186 * stops the loop from accepting new connections
187 */
188 public void disconnect()
189 {
190 connected = false;
191 }
192
193 /**starts the pipes for applets, or the server for applications
194 * @see java.lang.Runnable#run()
195 */
196 public void run()
197 {
198 if (pipedIn == null && pipedOut == null)
199 connect();
200 else
201 startPipes();
202 }
203
204 /**communicates with the client to determine which game
205 * to join
206 * @author Jam Jenkins
207 */
208 class Dispatcher extends Thread
209 {
210 /**
211 * the output stream to the client
212 */
213 ObjectOutputStream out;
214
215 /**
216 * the input stream from the client
217 */
218 ObjectInputStream in;
219
220 /**stores the streams for communication
221 * @param out the stream to the client
222 * @param in the stream from the client
223 */
224 public Dispatcher(ObjectOutputStream out, ObjectInputStream in)
225 {
226 this.out = out;
227 this.in = in;
228 }
229
230 /**responds to a join command. The format of the join command is:
231 * Join [gameName] [sessionName] [players]
232 * players is optional. If not specified, 2 players is assumed.
233 * @param command the line in the format described above
234 * @return true if successful, false otherwise. Joining can be
235 * unsuccessful if the format is not correct or if the game trying
236 * to join is full.
237 * @throws IOException if the communication chanel becomes corrupt
238 */
239 private boolean joinGame(String command) throws IOException
240 {
241 String[] parts = command.split(" ");
242 String gameName = parts[1];
243 String sessionName = parts[2];
244 String id = "Game: " + gameName + " Session: " + sessionName;
245 int players = 2;
246 if (parts.length > 3)
247 players = Math.max(1, Integer.parseInt(parts[3]));
248 GameConnections connections = games.get(id);
249 if (connections == null)
250 {
251 connections = new GameConnections(gameName, sessionName,
252 players);
253 games.put(id, connections);
254 }
255 if (connections.isFull())
256 {
257 out.writeObject("Game already full");
258 out.flush();
259 return false;
260 }
261 boolean results = connections.addConnection(out, in);
262 if (connections.isFull())
263 games.remove(id);
264 return results;
265 }
266
267 /**sends a list of the games currently in progress to the client.
268 * The format of the list is:
269 * Game: [gameName] Session: [sessionName]
270 * @throws IOException
271 */
272 private void listGames() throws IOException
273 {
274 String[] gameIDs = games.keySet().toArray(new String[0]);
275 String sum = "happy\n";
276 for (String name : gameIDs)
277 {
278 if (!games.get(name).isFull())
279 sum += name + "\n";
280 }
281 //java.lang.System.out.println("writing " + sum);
282 out.writeObject(sum);
283 out.flush();
284 //java.lang.System.out.println("flushed " + sum);
285 }
286
287 /**
288 * gets rid of games no longer in progress
289 */
290 private void cleanEmptyGames()
291 {
292 Set < Map.Entry < String, GameConnections >> gameSet = games.entrySet();
293 LinkedList<String> toRemove = new LinkedList<String>();
294 for (Map.Entry<String, GameConnections> game : gameSet)
295 {
296 if (!game.getValue().isActive())
297 toRemove.add(game.getKey());
298 }
299 for (String key : toRemove)
300 {
301 games.remove(key);
302 }
303 }
304
305 /**reads commands from the client and resonds appropriately
306 * the two commands are
307 * List Games
308 * and
309 * Join [gameName] [sessionName] [players]
310 * @see java.lang.Runnable#run()
311 */
312 public void run()
313 {
314 try
315 {
316 String command;
317 while (true)
318 {
319 //java.lang.System.out.println("awaiting command");
320 command = (String) in.readObject();
321 //java.lang.System.out.println("performing: " + command);
322 cleanEmptyGames();
323 if (command.startsWith("Join"))
324 {
325 if (joinGame(command))
326 break;
327 }
328 else if (command.startsWith("List Games"))
329 {
330 listGames();
331 }
332 else if (command.startsWith("Quit"))
333 return ;
334 }
335 // seems like a bug in the JDK, but this
336 // thread cannot end, or the Pipe breaks.
337 synchronized (this)
338 {
339 int players = Integer.parseInt(command.split(" ")[3]);
340 if (players == 1)
341 {
342 wait();
343 }
344 }
345 }
346 catch (Exception e)
347 {
348 e.printStackTrace();
349 }
350 }
351 }
352
353 /**starts the server accepting new connections
354 * @param argv not used
355 * @throws IOException
356 */
357 public static void main(String[] argv) throws IOException
358 {
359 new HttpServer().connect();
360 }
361 }