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
ConfigVariableInt::size
size_t size() const
Returns the number of unique words in the variable.
Definition: configVariableInt.I:79
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
Datagram::get_length
size_t get_length() const
Returns the number of bytes in the datagram.
Definition: datagram.I:335
Datagram::clear
virtual void clear()
Resets the datagram to empty, in preparation for building up a new datagram.
Definition: datagram.cxx:35
Datagram::append_data
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
Datagram::add_uint16
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
Datagram::get_data
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
Definition: datagram.I:327
TrueClock::get_global_ptr
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:68
config_downloader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagramIterator.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagram.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
httpChannel.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
socketStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.