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
size_t size() const
Returns the number of unique words in the variable.
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
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
size_t get_length() const
Returns the number of bytes in the datagram.
Definition: datagram.I:335
virtual void clear()
Resets the datagram to empty, in preparation for building up a new datagram.
Definition: datagram.cxx:35
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
Definition: datagram.I:327
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:68
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.