Panda3D
pStatReader.cxx
1 // Filename: pStatReader.cxx
2 // Created by: drose (09Jul00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pStatReader.h"
16 #include "pStatServer.h"
17 #include "pStatMonitor.h"
18 
19 #include "pStatClientControlMessage.h"
20 #include "pStatServerControlMessage.h"
21 #include "pStatFrameData.h"
22 #include "pStatProperties.h"
23 #include "datagram.h"
24 #include "datagramIterator.h"
25 #include "connectionManager.h"
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: PStatReader::Constructor
29 // Access: Public
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 PStatReader::
33 PStatReader(PStatServer *manager, PStatMonitor *monitor) :
34 #ifdef HAVE_THREADS
35  ConnectionReader(manager, monitor->is_thread_safe() ? 1 : 0),
36 #else // HAVE_THREADS
37  ConnectionReader(manager, 0),
38 #endif // HAVE_THREADS
39  _manager(manager),
40  _monitor(monitor),
41  _writer(manager, 0)
42 {
44  _writer.set_tcp_header_size(4);
45  _udp_port = 0;
46  _client_data = new PStatClientData(this);
47  _monitor->set_client_data(_client_data);
48 }
49 
50 ////////////////////////////////////////////////////////////////////
51 // Function: PStatReader::Destructor
52 // Access: Public
53 // Description:
54 ////////////////////////////////////////////////////////////////////
55 PStatReader::
56 ~PStatReader() {
57  _manager->release_udp_port(_udp_port);
58 }
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: PStatReader::close
62 // Access: Public
63 // Description: This will be called by the PStatClientData in
64 // response to its close() call. It will tell the
65 // server to let go of the reader so it can shut down
66 // its connection.
67 ////////////////////////////////////////////////////////////////////
68 void PStatReader::
69 close() {
70  _manager->remove_reader(_tcp_connection, this);
72 }
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: PStatReader::set_tcp_connection
76 // Access: Public
77 // Description: This is intended to be called only once, immediately
78 // after construction, by the PStatListener that created
79 // it. It tells the reader about the newly-established
80 // TCP connection to a client.
81 ////////////////////////////////////////////////////////////////////
82 void PStatReader::
83 set_tcp_connection(Connection *tcp_connection) {
84  _tcp_connection = tcp_connection;
85  add_connection(_tcp_connection);
86 
87  _udp_port = _manager->get_udp_port();
88  _udp_connection = _manager->open_UDP_connection(_udp_port);
89  while (_udp_connection.is_null()) {
90  // That UDP port was no good. Try another.
91  _udp_port = _manager->get_udp_port();
92  _udp_connection = _manager->open_UDP_connection(_udp_port);
93  }
94 
95  add_connection(_udp_connection);
96 
97  send_hello();
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: PStatReader::lost_connection
102 // Access: Public
103 // Description: This is called by the PStatServer when it detects
104 // that the connection has been lost. It should clean
105 // itself up and shut down nicely.
106 ////////////////////////////////////////////////////////////////////
107 void PStatReader::
109  _client_data->_is_alive = false;
110  _monitor->lost_connection();
111  _client_data.clear();
112 
113  _manager->close_connection(_tcp_connection);
114  _manager->close_connection(_udp_connection);
115  _tcp_connection.clear();
116  _udp_connection.clear();
117 }
118 
119 ////////////////////////////////////////////////////////////////////
120 // Function: PStatReader::idle
121 // Access: Public
122 // Description: Called each frame to do what needs to be done for the
123 // monitor's user-defined idle routines.
124 ////////////////////////////////////////////////////////////////////
125 void PStatReader::
126 idle() {
127  dequeue_frame_data();
128  _monitor->idle();
129 }
130 
131 ////////////////////////////////////////////////////////////////////
132 // Function: PStatReader::get_monitor
133 // Access: Public
134 // Description: Returns the monitor that this reader serves.
135 ////////////////////////////////////////////////////////////////////
138  return _monitor;
139 }
140 
141 ////////////////////////////////////////////////////////////////////
142 // Function: PStatReader::get_hostname
143 // Access: Private
144 // Description: Returns the current machine's hostname.
145 ////////////////////////////////////////////////////////////////////
146 string PStatReader::
147 get_hostname() {
148  if (_hostname.empty()) {
149  _hostname = ConnectionManager::get_host_name();
150  if (_hostname.empty()) {
151  _hostname = "unknown";
152  }
153  }
154  return _hostname;
155 }
156 
157 ////////////////////////////////////////////////////////////////////
158 // Function: PStatReader::send_hello
159 // Access: Private
160 // Description: Sends the initial greeting message to the client.
161 ////////////////////////////////////////////////////////////////////
162 void PStatReader::
163 send_hello() {
165  message._type = PStatServerControlMessage::T_hello;
166  message._server_hostname = get_hostname();
167  message._server_progname = _monitor->get_monitor_name();
168  message._udp_port = _udp_port;
169 
170  Datagram datagram;
171  message.encode(datagram);
172  _writer.send(datagram, _tcp_connection);
173 }
174 
175 ////////////////////////////////////////////////////////////////////
176 // Function: PStatReader::receive_datagram
177 // Access: Private, Virtual
178 // Description: Called by the net code whenever a new datagram is
179 // detected on a either the TCP or UDP connection.
180 ////////////////////////////////////////////////////////////////////
181 void PStatReader::
182 receive_datagram(const NetDatagram &datagram) {
183  Connection *connection = datagram.get_connection();
184 
185  if (connection == _tcp_connection) {
187  if (message.decode(datagram, _client_data)) {
188  handle_client_control_message(message);
189 
190  } else if (message._type == PStatClientControlMessage::T_datagram) {
191  handle_client_udp_data(datagram);
192 
193  } else {
194  nout << "Got unexpected message from client.\n";
195  }
196 
197  } else if (connection == _udp_connection) {
198  handle_client_udp_data(datagram);
199 
200  } else {
201  nout << "Got datagram from unexpected socket.\n";
202  }
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: PStatReader::handle_client_control_message
207 // Access: Private
208 // Description: Called when a control message has been received by
209 // the client over the TCP connection.
210 ////////////////////////////////////////////////////////////////////
211 void PStatReader::
212 handle_client_control_message(const PStatClientControlMessage &message) {
213  switch (message._type) {
214  case PStatClientControlMessage::T_hello:
215  {
216  _client_data->set_version(message._major_version, message._minor_version);
217  int server_major_version = get_current_pstat_major_version();
218  int server_minor_version = get_current_pstat_minor_version();
219 
220  if (message._major_version != server_major_version ||
221  (message._major_version == server_major_version &&
222  message._minor_version > server_minor_version)) {
223  _monitor->bad_version(message._client_hostname, message._client_progname,
224  message._major_version, message._minor_version,
225  server_major_version, server_minor_version);
226  _monitor->close();
227  } else {
228  _monitor->hello_from(message._client_hostname, message._client_progname);
229  }
230  }
231  break;
232 
233  case PStatClientControlMessage::T_define_collectors:
234  {
235  for (int i = 0; i < (int)message._collectors.size(); i++) {
236  _client_data->add_collector(message._collectors[i]);
237  _monitor->new_collector(message._collectors[i]->_index);
238  }
239  }
240  break;
241 
242  case PStatClientControlMessage::T_define_threads:
243  {
244  for (int i = 0; i < (int)message._names.size(); i++) {
245  int thread_index = message._first_thread_index + i;
246  string name = message._names[i];
247  _client_data->define_thread(thread_index, name);
248  _monitor->new_thread(thread_index);
249  }
250  }
251  break;
252 
253  default:
254  nout << "Invalid control message received from client.\n";
255  }
256 }
257 
258 ////////////////////////////////////////////////////////////////////
259 // Function: PStatReader::handle_client_udp_data
260 // Access: Private
261 // Description: Called when a UDP datagram has been received by the
262 // client. This should be a single frame's worth of
263 // data.
264 ////////////////////////////////////////////////////////////////////
265 void PStatReader::
266 handle_client_udp_data(const Datagram &datagram) {
267  if (!_monitor->is_client_known()) {
268  // If we haven't heard a "hello" from the client yet, we don't
269  // know what version data it will be sending us, so we can't
270  // decode the data. Chances are good we can't display it sensibly
271  // yet anyway. Ignore frame data until we get that hello.
272  return;
273  }
274 
275  DatagramIterator source(datagram);
276 
277  if (_client_data->is_at_least(2, 1)) {
278  // Throw away the zero byte at the beginning.
279  int initial_byte = source.get_uint8();
280  nassertv(initial_byte == 0);
281  }
282 
283  if (!_queued_frame_data.full()) {
284  FrameData data;
285  data._thread_index = source.get_uint16();
286  data._frame_number = source.get_uint32();
287  data._frame_data = new PStatFrameData;
288  data._frame_data->read_datagram(source, _client_data);
289 
290  // Queue up the data till we're ready to handle it in a
291  // single-threaded way.
292  _queued_frame_data.push_back(data);
293  }
294 }
295 
296 ////////////////////////////////////////////////////////////////////
297 // Function: PStatReader::dequeue_frame_data
298 // Access: Private
299 // Description: Called during the idle loop to pull out all the frame
300 // data that we might have read while the threaded
301 // reader was running.
302 ////////////////////////////////////////////////////////////////////
303 void PStatReader::
304 dequeue_frame_data() {
305  while (!_queued_frame_data.empty()) {
306  const FrameData &data = _queued_frame_data.front();
307  nassertv(_client_data != (PStatClientData *)NULL);
308 
309  // Check to see if any new collectors have level data.
310  int num_levels = data._frame_data->get_num_levels();
311  for (int i = 0; i < num_levels; i++) {
312  int collector_index = data._frame_data->get_level_collector(i);
313  if (!_client_data->get_collector_has_level(collector_index, data._thread_index)) {
314  // This collector is now reporting level data, and it wasn't
315  // before.
316  _client_data->set_collector_has_level(collector_index, data._thread_index, true);
317  _monitor->new_collector(collector_index);
318  }
319  }
320 
321  _client_data->record_new_frame(data._thread_index,
322  data._frame_number,
323  data._frame_data);
324  _monitor->new_data(data._thread_index, data._frame_number);
325 
326  _queued_frame_data.pop_front();
327  }
328 }
329 
PStatMonitor * get_monitor()
Returns the monitor that this reader serves.
void encode(Datagram &datagram) const
Writes the message into the indicated datagram.
void push_back(const Thing &t)
Adds an item to the end of the buffer.
Definition: circBuffer.I:207
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:43
bool send(const Datagram &datagram, const PT(Connection) &connection, bool block=false)
Enqueues a datagram for transmittal on the indicated socket.
void set_tcp_header_size(int tcp_header_size)
Sets the header size of TCP packets.
void set_tcp_connection(Connection *tcp_connection)
This is intended to be called only once, immediately after construction, by the PStatListener that cr...
Definition: pStatReader.cxx:83
bool empty() const
Returns true if the buffer is empty.
Definition: circBuffer.I:66
This kind of message is sent from the server to the client on the TCP socket to establish critical co...
The data associated with a particular client, but not with any one particular frame or thread: the li...
This kind of message is sent from the client to the server on the TCP socket to establish critical co...
int get_udp_port()
Returns a new port number that will probably be free to use as a UDP port.
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
Definition: circBuffer.I:81
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
void idle()
Called each frame to do what needs to be done for the monitor&#39;s user-defined idle routines...
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
This is an abstract base class for a family of classes that listen for activity on a socket and respo...
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:43
bool decode(const Datagram &datagram, PStatClientVersion *version)
Extracts the message from the indicated datagram.
void lost_connection()
This is called by the PStatServer when it detects that the connection has been lost.
void read_datagram(DatagramIterator &source, PStatClientVersion *version)
Extracts the FrameData definition from the datagram.
void pop_front()
Removes the first item from the buffer.
Definition: circBuffer.I:152
Contains the raw timing and level data for a single frame.
void remove_reader(Connection *connection, PStatReader *reader)
Removes the indicated reader.
static string get_host_name()
Returns the name of this particular machine on the network, if available, or the empty string if the ...
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
The overall manager of the network connections.
Definition: pStatServer.h:41
A class to retrieve the individual data elements previously stored in a Datagram. ...
const Thing & front() const
Returns a reference to the first item in the queue.
Definition: circBuffer.I:96
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:32
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void release_udp_port(int port)
Indicates that the given UDP port is once again free for use.
bool close_connection(const PT(Connection) &connection)
Terminates a UDP or TCP socket previously opened.
void set_tcp_header_size(int tcp_header_size)
Sets the header size of TCP packets.
void close()
This will be called by the PStatClientData in response to its close() call.
Definition: pStatReader.cxx:69