Panda3D
socketStream.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 socketStream.cxx
10  * @author drose
11  * @date 2002-10-19
12  */
13 
14 #include "socketStream.h"
15 #include "datagram.h"
16 #include "datagramIterator.h"
17 #include "httpChannel.h"
18 #include "config_downloader.h"
19 
20 #ifdef HAVE_OPENSSL
21 
22 /**
23  *
24  */
25 SSReader::
26 SSReader(std::istream *stream) : _istream(stream) {
27  _data_expected = 0;
28  _tcp_header_size = tcp_header_size;
29 
30 #ifdef SIMULATE_NETWORK_DELAY
31  _delay_active = false;
32  _min_delay = 0.0;
33  _delay_variance = 0.0;
34 #endif // SIMULATE_NETWORK_DELAY
35 }
36 
37 /**
38  *
39  */
40 SSReader::
41 ~SSReader() {
42 }
43 
44 /**
45  * Receives a datagram over the socket by expecting a little-endian 16-bit
46  * byte count as a prefix. If the socket stream is non-blocking, may return
47  * false if the data is not available; otherwise, returns false only if the
48  * socket closes.
49  */
50 bool SSReader::
51 do_receive_datagram(Datagram &dg) {
52  if (_tcp_header_size == 0) {
53  _data_expected = _data_so_far.size();
54  }
55  if (_data_expected == 0) {
56  // Read the first two bytes: the datagram length.
57  while ((int)_data_so_far.size() < _tcp_header_size) {
58  int ch = _istream->get();
59  if (ch == EOF || _istream->fail()) {
60  _istream->clear();
61  return false;
62  }
63  _data_so_far.push_back((unsigned char)ch);
64  }
65 
66  Datagram header(_data_so_far);
67  DatagramIterator di(header);
68  if (_tcp_header_size == 2) {
69  _data_expected = di.get_uint16();
70  } else if (_tcp_header_size == 4) {
71  _data_expected = di.get_uint32();
72  }
73  _data_so_far.erase(_data_so_far.begin(), _data_so_far.begin() + _tcp_header_size);
74 
75  if (_data_expected == 0) {
76  // Empty datagram.
77  dg.clear();
78  return true;
79  }
80  }
81 
82  // Read the next n bytes until the datagram is filled.
83 
84  static const size_t buffer_size = 1024;
85  char buffer[buffer_size];
86 
87  size_t read_count = std::min(_data_expected - _data_so_far.size(), buffer_size);
88  _istream->read(buffer, read_count);
89  size_t count = _istream->gcount();
90  while (count != 0) {
91  _data_so_far.insert(_data_so_far.end(), buffer, buffer + count);
92 
93  read_count = std::min(_data_expected - _data_so_far.size(),
94  buffer_size);
95  _istream->read(buffer, read_count);
96  count = _istream->gcount();
97  }
98 
99  if (_data_so_far.size() < _data_expected) {
100  // Not yet here. Clear the istream error flag and return false to
101  // indicate more coming.
102  _istream->clear();
103  return false;
104  }
105 
106  dg.clear();
107  dg.append_data(_data_so_far);
108 
109  _data_expected = 0;
110  _data_so_far.clear();
111 
112  return true;
113 }
114 
115 #ifdef SIMULATE_NETWORK_DELAY
116 /**
117  * Enables a simulated network latency. All datagrams received from this
118  * point on will be held for a random interval of least min_delay seconds, and
119  * no more than max_delay seconds, before being visible. It is as if
120  * datagrams suddenly took much longer to arrive.
121  *
122  * This should *only* be called if the underlying socket is non-blocking. If
123  * you call this on a blocking socket, it will force all datagrams to be held
124  * up until the socket closes.
125  */
126 void SSReader::
127 start_delay(double min_delay, double max_delay) {
128  _min_delay = min_delay;
129  _delay_variance = std::max(max_delay - min_delay, 0.0);
130  _delay_active = true;
131 }
132 #endif // SIMULATE_NETWORK_DELAY
133 
134 #ifdef SIMULATE_NETWORK_DELAY
135 /**
136  * Disables the simulated network latency started by a previous call to
137  * start_delay(). Datagrams will once again be visible as soon as they are
138  * received.
139  */
140 void SSReader::
141 stop_delay() {
142  _delay_active = false;
143 }
144 #endif // SIMULATE_NETWORK_DELAY
145 
146 #ifdef SIMULATE_NETWORK_DELAY
147 /**
148  * Adds the datagram to the delay queue for a random time interval.
149  */
150 void SSReader::
151 delay_datagram(const Datagram &datagram) {
152  nassertv(_delay_active);
153 
154  double now = TrueClock::get_global_ptr()->get_short_time();
155  double reveal_time = now + _min_delay;
156 
157  if (_delay_variance > 0.0) {
158  reveal_time += _delay_variance * ((double)rand() / (double)RAND_MAX);
159  }
160  _delayed.push_back(DelayedDatagram());
161  DelayedDatagram &dd = _delayed.back();
162  dd._reveal_time = reveal_time;
163  dd._datagram = datagram;
164 }
165 #endif // SIMULATE_NETWORK_DELAY
166 
167 #ifdef SIMULATE_NETWORK_DELAY
168 /**
169  * Checks the delayed queue for any now available datagrams. If any are
170  * available, returns true and fills datagram with its value.
171  */
172 bool SSReader::
173 get_delayed(Datagram &datagram) {
174  if (_delayed.empty()) {
175  return false;
176  }
177  const DelayedDatagram &dd = _delayed.front();
178  if (_delay_active) {
179  double now = TrueClock::get_global_ptr()->get_short_time();
180  if (dd._reveal_time > now) {
181  // Not yet.
182  return false;
183  }
184  }
185 
186  datagram = dd._datagram;
187  _delayed.pop_front();
188 
189  return true;
190 }
191 #endif // SIMULATE_NETWORK_DELAY
192 
193 /**
194  *
195  */
196 SSWriter::
197 SSWriter(std::ostream *stream) : _ostream(stream) {
198  _collect_tcp = collect_tcp;
199  _collect_tcp_interval = collect_tcp_interval;
200  _queued_data_start = 0.0;
201  _tcp_header_size = tcp_header_size;
202 }
203 
204 /**
205  *
206  */
207 SSWriter::
208 ~SSWriter() {
209 }
210 
211 /**
212  * Transmits the indicated datagram over the socket by prepending it with a
213  * little-endian 16-bit byte count. Does not return until the data is sent or
214  * the connection is closed, even if the socket stream is non-blocking.
215  */
216 bool SSWriter::
217 send_datagram(const Datagram &dg) {
218  Datagram header;
219  if (_tcp_header_size == 2) {
220  if (dg.get_length() >= 0x10000) {
221  downloader_cat.error()
222  << "Attempt to send TCP datagram of " << dg.get_length()
223  << " bytes--too long!\n";
224  nassert_raise("Datagram too long");
225  return false;
226  }
227 
228  header.add_uint16(dg.get_length());
229  } else if (_tcp_header_size == 4) {
230  header.add_uint32(dg.get_length());
231  }
232 
233  // These two writes don't generate two socket calls, because the socket
234  // stream is always buffered.
235  _ostream->write((const char *)header.get_data(), header.get_length());
236  _ostream->write((const char *)dg.get_data(), dg.get_length());
237 
238  // Now flush the buffer immediately, forcing the data to be sent (unless
239  // collect-tcp mode is in effect).
240  flush();
241 
242  return !is_closed();
243 }
244 
245 /**
246  *
247  */
248 ISocketStream::
249 ~ISocketStream() {
250  // This should already have been cleared by the subclass destructor.
251  nassertv(_channel == nullptr);
252 }
253 
254 #endif // HAVE_OPENSSL
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:68
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void clear()
Resets the datagram to empty, in preparation for building up a new datagram.
Definition: datagram.cxx:35
size_t size() const
Returns the number of unique words in the variable.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
size_t get_length() const
Returns the number of bytes in the datagram.
Definition: datagram.I:335
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
Definition: datagram.I:327