View Javadoc

1   /*
2    * Created on 2004-12-08
3    *
4    * ClientManager - klasa odpowiada za przechowywanie informacji o połaczonych klientach, oraz
5    * obsługę komunikacji z nimi
6    */
7   package org.sourceforge.jvb3d.Network;
8   
9   import java.io.IOException;
10  import java.net.InetSocketAddress;
11  import java.net.SocketException;
12  import java.util.HashMap;
13  import java.util.Iterator;
14  import java.util.LinkedList;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Observable;
18  import java.util.Observer;
19  import java.util.logging.Logger;
20  
21  /***
22   * @author Łukasz Krzyżak
23   * 
24   * Klasa odpowiada za obsługę komunikacji pomiędzy serwerem a klientami. Odbiera
25   * pakiety wysyłane przez klientów, analizuje i przekazuje do dalszej obróbki,
26   * oraz rozsyła informacje od serwera do klientów
27   */
28  public class ClientManager implements Observer {
29  	private Map<InetSocketAddress, ClientData> clients = new HashMap<InetSocketAddress, ClientData>();
30  
31  	private NetworkServerFacade serverFacade = null;
32  
33  	private ClientFactory clientFactory = null;
34  
35  	private Logger fLogger = Logger.getLogger("ClientManager");
36  
37  	private List<InetSocketAddress> timeoutedClients = new LinkedList<InetSocketAddress>();
38  	
39  	/***
40  	 * pobiera referencję do fasady
41  	 *
42  	 */
43  	public ClientManager() {
44  		serverFacade = NetworkServerFacadeHolder.getNetworkFacade();
45  		clientFactory = serverFacade.getClientFactory();
46  	}
47  
48  	/***
49  	 * metoda update - uruchamiana, gdy serwer otrzyma pakiet
50  	 * 
51  	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
52  	 */
53  	public void update(Observable arg0, Object arg1) {
54  		// bardzo brzydki i nieobiektowy kawałek kodu ;)
55  		try {
56  			Packet recvPacket = ((ReceivedPacketBuffer) arg0).getNextPacket();
57  			if (recvPacket instanceof JoinPacket)
58  				processJoinPacket((JoinPacket) recvPacket);
59  			else if (recvPacket instanceof UpdatePacket)
60  				processUpdatePacket((UpdatePacket) recvPacket);
61  			else
62  				fLogger.warning("Odebrano pakiet nieznanego typu: "
63  						+ recvPacket.getClass().getName());
64  		} catch (Exception e) {
65  			fLogger.severe("Złapano wyjątek - " + e.toString());
66  		}
67  	}
68  
69  	/***
70  	 * rozsyła dane podane jako parametr do wszystkich klientów. Sprawdza
71  	 * timeouty klientów, i powoduje usunięcie tych u których one występują.
72  	 * 
73  	 * @param updateData dane do wysłania
74  	 * @throws IOException jeśli wystąpił błąd socketu
75  	 */
76  	public void sendUpdate(byte[] updateData) throws IOException {
77  		if (clients.size() == 0) {
78  			fLogger.fine("Brak klientów do wysłania update");
79  		} else {
80  			Iterator<ClientData> clientIterator = clients.values().iterator();
81  			timeoutedClients.clear();
82  			
83  			while (clientIterator.hasNext()) {
84  				try {
85  					clientIterator.next().sendUpdate(updateData);
86  				} catch (TimeoutException e) {
87  					e.printStackTrace();
88  					ClientData timeoutedClient = clients.get(e.getAddress());
89  					serverFacade.forwardClientTimeout(timeoutedClient
90  							.getAvatarID());
91  					timeoutedClients.add(e.getAddress());
92  					fLogger.warning("Utracono połączenie z klientem"
93  							+ timeoutedClient.getClientAddress());
94  				}
95  			}
96  			
97  			for(InetSocketAddress client : timeoutedClients)
98  				clients.remove(client);
99  		}
100 	}
101 
102 	/***
103 	 * metoda obsluguje pakiet JoinPacket: - jeśli klient o takim adresie już
104 	 * istnieje - rzucany jest wyjątek IllegalArgumentException - jesli klient
105 	 * nie istnieje - tworzony jest nowy, i dodawany do listy
106 	 * 
107 	 * @param packet
108 	 *            pakiet otrzymany od klienta
109 	 * @throws IllegalArgumentException
110 	 *             jeśli klient już był wcześniej dodany
111 	 * @throws SocketException jeśli wystąpił bład socketu
112 	 */
113 	private void processJoinPacket(JoinPacket packet) throws SocketException {
114 		if (clients.containsKey(packet.getSourceAddress())) {
115 			fLogger.warning("Klient z " + packet.getSourceAddress()
116 					+ " próbuje połączyć się drugi raz");
117 			throw new IllegalArgumentException();
118 		}
119 		ClientData client = clientFactory.createClient(packet
120 				.getSourceAddress());
121 		clients.put(packet.getSourceAddress(), client);
122 		fLogger.fine("Dodano klienta z " + packet.getSourceAddress());
123 	}
124 
125 	/***
126 	 * obsługuje pakiet typu "update". Sprawdza w obiekcie z którego otrzymano
127 	 * pakiet czy jest poprawny, jeśli tak pakiet jest forwardowany na fasadę,
128 	 * jeśli nie umieszczany jest wpis w logach a pakiet idzie do kosza.
129 	 * 
130 	 * @param packet
131 	 *            pakiet update otrzymany z sieci
132 	 */
133 	private void processUpdatePacket(UpdatePacket packet) {
134 		ClientData client = clients.get(packet.getSourceAddress());
135 		if (client == null)
136 			fLogger.severe("Klient na " + packet.getSourceAddress()
137 					+ " nie istnieje");
138 		else {
139 			if (client.validatePacket(packet))
140 				serverFacade.forwardUpdate(packet);
141 			else
142 				fLogger.warning("Klient na " + packet.getSourceAddress()
143 						+ " przysłał pakiet ze złym numerem seq");
144 		}
145 	}
146 
147 	/***
148 	 * metoda zwraca ilość połączonych klientów
149 	 * 
150 	 * @return ilość połaczonych klientów
151 	 */
152 	public int getClientCount() {
153 		return clients.size();
154 	}
155 }
156