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