Source code: marauroa/net/NetworkClientManager.java
1 /* $Id: NetworkClientManager.java,v 1.9 2003/12/08 23:40:10 arianne_rpg Exp $ */
2 /***************************************************************************
3 * (C) Copyright 2003 - Marauroa *
4 ***************************************************************************
5 ***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
13 package marauroa.net;
14
15 import java.net.*;
16 import java.util.*;
17 import java.io.*;
18 import marauroa.*;
19
20 /** The NetworkClientManager is in charge of sending and recieving the packages
21 * from the network. */
22 public class NetworkClientManager
23 {
24 private DatagramSocket socket;
25 private InetSocketAddress address;
26 private int clientid;
27
28 private MessageFactory msgFactory;
29
30 static private class PacketContainer
31 {
32 public byte signature;
33 public byte remaining;
34 public byte[] content;
35 public InetSocketAddress address;
36 public Date timestamp;
37 }
38
39 private Map pendingPackets;
40
41 /** Constructor that opens the socket on the marauroa_PORT and start the thread
42 to recieve new messages from the network. */
43 public NetworkClientManager(String host) throws SocketException
44 {
45 clientid=0;
46 address=new InetSocketAddress(host,NetConst.marauroa_PORT);
47 socket=new DatagramSocket();
48 socket.setSoTimeout(TimeoutConf.SOCKET_TIMEOUT);
49
50 msgFactory=MessageFactory.getFactory();
51 pendingPackets=new HashMap();
52 }
53
54 /** This method notify the thread to finish it execution */
55 public void finish()
56 {
57 socket.close();
58 }
59
60 /** This method returns a message if it is available or null
61 * @return a Message*/
62 public Message getMessage()
63 {
64 try
65 {
66 Iterator it=pendingPackets.entrySet().iterator();
67 while(it.hasNext())
68 {
69 Map.Entry entry=(Map.Entry)it.next();
70 PacketContainer message=(PacketContainer)entry.getValue();
71 if(message.remaining==0)
72 {
73 Message msg=msgFactory.getMessage(message.content,message.address);
74 System.out.println("NetworkClientManager: receive message("+msg.getType()+") from "+msg.getClientID());
75
76 if(msg.getType()==Message.TYPE_S2C_LOGIN_ACK)
77 {
78 clientid=msg.getClientID();
79 }
80
81 pendingPackets.remove(new Byte(message.signature));
82 return msg;
83 }
84
85 if(new Date().getTime()-message.timestamp.getTime()>TimeoutConf.CLIENT_MESSAGE_DROPPED_TIMEOUT)
86 {
87 System.out.println("NetworkClientManager: deleted incompleted message after timedout");
88 pendingPackets.remove(new Byte(message.signature));
89 }
90 }
91 }
92 catch(IOException e)
93 {
94 /* Report the exception */
95 marauroad.report(e.getMessage());
96 return null;
97 }
98
99 byte[] buffer=new byte[NetConst.UDP_PACKET_SIZE];
100 DatagramPacket packet=new DatagramPacket(buffer,buffer.length);
101 int i=0;
102
103 try
104 {
105 /** We want to avoid this to block the whole client recieving messages */
106 while(i<TimeoutConf.CLIENT_NETWORK_NUM_READ)
107 {
108 ++i;
109
110 socket.receive(packet);
111 byte[] data=packet.getData();
112
113 /* A multipart message. We try to read the rest now.
114 * We need to check on the list if the message exist and it exist we add this one. */
115 /* TODO: Looks like hardcoded, write it in a better way */
116 byte total=data[0];
117 byte position=data[1];
118 byte signature=data[2];
119 System.out.println("NetworkClientManager: receive multipart message("+signature+"): "+(position+1)+" of "+total);
120
121 if(!pendingPackets.containsKey(new Byte(signature)))
122 {
123 /** This is the first packet */
124 PacketContainer message=new PacketContainer();
125 message.signature=signature;
126 message.remaining=(byte)(total-1);
127 message.address=(InetSocketAddress)packet.getSocketAddress();
128 message.content=new byte[(NetConst.UDP_PACKET_SIZE-3)*total];
129 message.timestamp=new Date();
130
131 System.arraycopy(data,3,message.content,(NetConst.UDP_PACKET_SIZE-3)*position,data.length-3);
132 pendingPackets.put(new Byte(signature),message);
133 }
134 else
135 {
136 PacketContainer message=(PacketContainer)pendingPackets.get(new Byte(signature));
137 --message.remaining;
138
139 if(message.remaining<0)
140 {
141 System.out.println("ERROR: We confused the messages");
142 return null;
143 }
144
145 System.arraycopy(data,3,message.content,(NetConst.UDP_PACKET_SIZE-3)*position,data.length-3);
146 }
147 }
148
149 return null;
150 }
151 catch(java.net.SocketTimeoutException e)
152 {
153 /* We need the thread to check from time to time if user has requested an exit */
154 return null;
155 }
156 catch(IOException e)
157 {
158 /* Report the exception */
159 marauroad.report(e.getMessage());
160 return null;
161 }
162 }
163
164 /** This method add a message to be delivered to the client the message is pointed to.
165 * @param msg the message to ve delivered. */
166 public synchronized void addMessage(Message msg)
167 {
168 try
169 {
170 /* We enforce the remote endpoint */
171 msg.setAddress(address);
172 msg.setClientID(clientid);
173
174 ByteArrayOutputStream out=new ByteArrayOutputStream();
175 OutputSerializer s=new OutputSerializer(out);
176
177 System.out.println("NetworkClientManager: send message("+msg.getType()+") from "+msg.getClientID());
178 s.write(msg);
179
180 byte[] buffer=out.toByteArray();
181 DatagramPacket pkt=new DatagramPacket(buffer,buffer.length,msg.getAddress());
182
183 socket.send(pkt);
184 }
185 catch(IOException e)
186 {
187 /* Report the exception */
188 marauroad.report(e.getMessage());
189 }
190 }
191 }