rob@76
|
1 /*
|
rob@76
|
2 oscpack -- Open Sound Control (OSC) packet manipulation library
|
rob@76
|
3 http://www.rossbencina.com/code/oscpack
|
rob@76
|
4
|
rob@76
|
5 Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
|
rob@76
|
6
|
rob@76
|
7 Permission is hereby granted, free of charge, to any person obtaining
|
rob@76
|
8 a copy of this software and associated documentation files
|
rob@76
|
9 (the "Software"), to deal in the Software without restriction,
|
rob@76
|
10 including without limitation the rights to use, copy, modify, merge,
|
rob@76
|
11 publish, distribute, sublicense, and/or sell copies of the Software,
|
rob@76
|
12 and to permit persons to whom the Software is furnished to do so,
|
rob@76
|
13 subject to the following conditions:
|
rob@76
|
14
|
rob@76
|
15 The above copyright notice and this permission notice shall be
|
rob@76
|
16 included in all copies or substantial portions of the Software.
|
rob@76
|
17
|
rob@76
|
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
rob@76
|
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
rob@76
|
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
rob@76
|
21 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
rob@76
|
22 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
rob@76
|
23 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
rob@76
|
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
rob@76
|
25 */
|
rob@76
|
26
|
rob@76
|
27 /*
|
rob@76
|
28 The text above constitutes the entire oscpack license; however,
|
rob@76
|
29 the oscpack developer(s) also make the following non-binding requests:
|
rob@76
|
30
|
rob@76
|
31 Any person wishing to distribute modifications to the Software is
|
rob@76
|
32 requested to send the modifications to the original developer so that
|
rob@76
|
33 they can be incorporated into the canonical version. It is also
|
rob@76
|
34 requested that these non-binding requests be included whenever the
|
rob@76
|
35 above license is reproduced.
|
rob@76
|
36 */
|
rob@76
|
37
|
rob@76
|
38 #include <winsock2.h> // this must come first to prevent errors with MSVC7
|
rob@76
|
39 #include <windows.h>
|
rob@76
|
40 #include <mmsystem.h> // for timeGetTime()
|
rob@76
|
41
|
rob@76
|
42 #ifndef WINCE
|
rob@76
|
43 #include <signal.h>
|
rob@76
|
44 #endif
|
rob@76
|
45
|
rob@76
|
46 #include <algorithm>
|
rob@76
|
47 #include <cassert>
|
rob@76
|
48 #include <cstring> // for memset
|
rob@76
|
49 #include <stdexcept>
|
rob@76
|
50 #include <vector>
|
rob@76
|
51
|
rob@76
|
52 #include "ip/UdpSocket.h" // usually I'd include the module header first
|
rob@76
|
53 // but this is causing conflicts with BCB4 due to
|
rob@76
|
54 // std::size_t usage.
|
rob@76
|
55
|
rob@76
|
56 #include "ip/NetworkingUtils.h"
|
rob@76
|
57 #include "ip/PacketListener.h"
|
rob@76
|
58 #include "ip/TimerListener.h"
|
rob@76
|
59
|
rob@76
|
60
|
rob@76
|
61 typedef int socklen_t;
|
rob@76
|
62
|
rob@76
|
63
|
rob@76
|
64 static void SockaddrFromIpEndpointName( struct sockaddr_in& sockAddr, const IpEndpointName& endpoint )
|
rob@76
|
65 {
|
rob@76
|
66 std::memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
|
rob@76
|
67 sockAddr.sin_family = AF_INET;
|
rob@76
|
68
|
rob@76
|
69 sockAddr.sin_addr.s_addr =
|
rob@76
|
70 (endpoint.address == IpEndpointName::ANY_ADDRESS)
|
rob@76
|
71 ? INADDR_ANY
|
rob@76
|
72 : htonl( endpoint.address );
|
rob@76
|
73
|
rob@76
|
74 sockAddr.sin_port =
|
rob@76
|
75 (endpoint.port == IpEndpointName::ANY_PORT)
|
rob@76
|
76 ? (short)0
|
rob@76
|
77 : htons( (short)endpoint.port );
|
rob@76
|
78 }
|
rob@76
|
79
|
rob@76
|
80
|
rob@76
|
81 static IpEndpointName IpEndpointNameFromSockaddr( const struct sockaddr_in& sockAddr )
|
rob@76
|
82 {
|
rob@76
|
83 return IpEndpointName(
|
rob@76
|
84 (sockAddr.sin_addr.s_addr == INADDR_ANY)
|
rob@76
|
85 ? IpEndpointName::ANY_ADDRESS
|
rob@76
|
86 : ntohl( sockAddr.sin_addr.s_addr ),
|
rob@76
|
87 (sockAddr.sin_port == 0)
|
rob@76
|
88 ? IpEndpointName::ANY_PORT
|
rob@76
|
89 : ntohs( sockAddr.sin_port )
|
rob@76
|
90 );
|
rob@76
|
91 }
|
rob@76
|
92
|
rob@76
|
93
|
rob@76
|
94 class UdpSocket::Implementation{
|
rob@76
|
95 NetworkInitializer networkInitializer_;
|
rob@76
|
96
|
rob@76
|
97 bool isBound_;
|
rob@76
|
98 bool isConnected_;
|
rob@76
|
99
|
rob@76
|
100 SOCKET socket_;
|
rob@76
|
101 struct sockaddr_in connectedAddr_;
|
rob@76
|
102 struct sockaddr_in sendToAddr_;
|
rob@76
|
103
|
rob@76
|
104 public:
|
rob@76
|
105
|
rob@76
|
106 Implementation()
|
rob@76
|
107 : isBound_( false )
|
rob@76
|
108 , isConnected_( false )
|
rob@76
|
109 , socket_( INVALID_SOCKET )
|
rob@76
|
110 {
|
rob@76
|
111 if( (socket_ = socket( AF_INET, SOCK_DGRAM, 0 )) == INVALID_SOCKET ){
|
rob@76
|
112 throw std::runtime_error("unable to create udp socket\n");
|
rob@76
|
113 }
|
rob@76
|
114
|
rob@76
|
115 std::memset( &sendToAddr_, 0, sizeof(sendToAddr_) );
|
rob@76
|
116 sendToAddr_.sin_family = AF_INET;
|
rob@76
|
117 }
|
rob@76
|
118
|
rob@76
|
119 ~Implementation()
|
rob@76
|
120 {
|
rob@76
|
121 if (socket_ != INVALID_SOCKET) closesocket(socket_);
|
rob@76
|
122 }
|
rob@76
|
123
|
rob@76
|
124 void SetEnableBroadcast( bool enableBroadcast )
|
rob@76
|
125 {
|
rob@76
|
126 char broadcast = (char)((enableBroadcast) ? 1 : 0); // char on win32
|
rob@76
|
127 setsockopt(socket_, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
|
rob@76
|
128 }
|
rob@76
|
129
|
rob@76
|
130 void SetAllowReuse( bool allowReuse )
|
rob@76
|
131 {
|
rob@76
|
132 // Note: SO_REUSEADDR is non-deterministic for listening sockets on Win32. See MSDN article:
|
rob@76
|
133 // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
|
rob@76
|
134 // http://msdn.microsoft.com/en-us/library/ms740621%28VS.85%29.aspx
|
rob@76
|
135
|
rob@76
|
136 char reuseAddr = (char)((allowReuse) ? 1 : 0); // char on win32
|
rob@76
|
137 setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
rob@76
|
138 }
|
rob@76
|
139
|
rob@76
|
140 IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
|
rob@76
|
141 {
|
rob@76
|
142 assert( isBound_ );
|
rob@76
|
143
|
rob@76
|
144 // first connect the socket to the remote server
|
rob@76
|
145
|
rob@76
|
146 struct sockaddr_in connectSockAddr;
|
rob@76
|
147 SockaddrFromIpEndpointName( connectSockAddr, remoteEndpoint );
|
rob@76
|
148
|
rob@76
|
149 if (connect(socket_, (struct sockaddr *)&connectSockAddr, sizeof(connectSockAddr)) < 0) {
|
rob@76
|
150 throw std::runtime_error("unable to connect udp socket\n");
|
rob@76
|
151 }
|
rob@76
|
152
|
rob@76
|
153 // get the address
|
rob@76
|
154
|
rob@76
|
155 struct sockaddr_in sockAddr;
|
rob@76
|
156 std::memset( (char *)&sockAddr, 0, sizeof(sockAddr ) );
|
rob@76
|
157 socklen_t length = sizeof(sockAddr);
|
rob@76
|
158 if (getsockname(socket_, (struct sockaddr *)&sockAddr, &length) < 0) {
|
rob@76
|
159 throw std::runtime_error("unable to getsockname\n");
|
rob@76
|
160 }
|
rob@76
|
161
|
rob@76
|
162 if( isConnected_ ){
|
rob@76
|
163 // reconnect to the connected address
|
rob@76
|
164
|
rob@76
|
165 if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
|
rob@76
|
166 throw std::runtime_error("unable to connect udp socket\n");
|
rob@76
|
167 }
|
rob@76
|
168
|
rob@76
|
169 }else{
|
rob@76
|
170 // unconnect from the remote address
|
rob@76
|
171
|
rob@76
|
172 struct sockaddr_in unconnectSockAddr;
|
rob@76
|
173 SockaddrFromIpEndpointName( unconnectSockAddr, IpEndpointName() );
|
rob@76
|
174
|
rob@76
|
175 if( connect(socket_, (struct sockaddr *)&unconnectSockAddr, sizeof(unconnectSockAddr)) < 0
|
rob@76
|
176 && WSAGetLastError() != WSAEADDRNOTAVAIL ){
|
rob@76
|
177 throw std::runtime_error("unable to un-connect udp socket\n");
|
rob@76
|
178 }
|
rob@76
|
179 }
|
rob@76
|
180
|
rob@76
|
181 return IpEndpointNameFromSockaddr( sockAddr );
|
rob@76
|
182 }
|
rob@76
|
183
|
rob@76
|
184 void Connect( const IpEndpointName& remoteEndpoint )
|
rob@76
|
185 {
|
rob@76
|
186 SockaddrFromIpEndpointName( connectedAddr_, remoteEndpoint );
|
rob@76
|
187
|
rob@76
|
188 if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) {
|
rob@76
|
189 throw std::runtime_error("unable to connect udp socket\n");
|
rob@76
|
190 }
|
rob@76
|
191
|
rob@76
|
192 isConnected_ = true;
|
rob@76
|
193 }
|
rob@76
|
194
|
rob@76
|
195 void Send( const char *data, std::size_t size )
|
rob@76
|
196 {
|
rob@76
|
197 assert( isConnected_ );
|
rob@76
|
198
|
rob@76
|
199 send( socket_, data, (int)size, 0 );
|
rob@76
|
200 }
|
rob@76
|
201
|
rob@76
|
202 void SendTo( const IpEndpointName& remoteEndpoint, const char *data, std::size_t size )
|
rob@76
|
203 {
|
rob@76
|
204 sendToAddr_.sin_addr.s_addr = htonl( remoteEndpoint.address );
|
rob@76
|
205 sendToAddr_.sin_port = htons( (short)remoteEndpoint.port );
|
rob@76
|
206
|
rob@76
|
207 sendto( socket_, data, (int)size, 0, (sockaddr*)&sendToAddr_, sizeof(sendToAddr_) );
|
rob@76
|
208 }
|
rob@76
|
209
|
rob@76
|
210 void Bind( const IpEndpointName& localEndpoint )
|
rob@76
|
211 {
|
rob@76
|
212 struct sockaddr_in bindSockAddr;
|
rob@76
|
213 SockaddrFromIpEndpointName( bindSockAddr, localEndpoint );
|
rob@76
|
214
|
rob@76
|
215 if (bind(socket_, (struct sockaddr *)&bindSockAddr, sizeof(bindSockAddr)) < 0) {
|
rob@76
|
216 throw std::runtime_error("unable to bind udp socket\n");
|
rob@76
|
217 }
|
rob@76
|
218
|
rob@76
|
219 isBound_ = true;
|
rob@76
|
220 }
|
rob@76
|
221
|
rob@76
|
222 bool IsBound() const { return isBound_; }
|
rob@76
|
223
|
rob@76
|
224 std::size_t ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, std::size_t size )
|
rob@76
|
225 {
|
rob@76
|
226 assert( isBound_ );
|
rob@76
|
227
|
rob@76
|
228 struct sockaddr_in fromAddr;
|
rob@76
|
229 socklen_t fromAddrLen = sizeof(fromAddr);
|
rob@76
|
230
|
rob@76
|
231 int result = recvfrom(socket_, data, (int)size, 0,
|
rob@76
|
232 (struct sockaddr *) &fromAddr, (socklen_t*)&fromAddrLen);
|
rob@76
|
233 if( result < 0 )
|
rob@76
|
234 return 0;
|
rob@76
|
235
|
rob@76
|
236 remoteEndpoint.address = ntohl(fromAddr.sin_addr.s_addr);
|
rob@76
|
237 remoteEndpoint.port = ntohs(fromAddr.sin_port);
|
rob@76
|
238
|
rob@76
|
239 return result;
|
rob@76
|
240 }
|
rob@76
|
241
|
rob@76
|
242 SOCKET& Socket() { return socket_; }
|
rob@76
|
243 };
|
rob@76
|
244
|
rob@76
|
245 UdpSocket::UdpSocket()
|
rob@76
|
246 {
|
rob@76
|
247 impl_ = new Implementation();
|
rob@76
|
248 }
|
rob@76
|
249
|
rob@76
|
250 UdpSocket::~UdpSocket()
|
rob@76
|
251 {
|
rob@76
|
252 delete impl_;
|
rob@76
|
253 }
|
rob@76
|
254
|
rob@76
|
255 void UdpSocket::SetEnableBroadcast( bool enableBroadcast )
|
rob@76
|
256 {
|
rob@76
|
257 impl_->SetEnableBroadcast( enableBroadcast );
|
rob@76
|
258 }
|
rob@76
|
259
|
rob@76
|
260 void UdpSocket::SetAllowReuse( bool allowReuse )
|
rob@76
|
261 {
|
rob@76
|
262 impl_->SetAllowReuse( allowReuse );
|
rob@76
|
263 }
|
rob@76
|
264
|
rob@76
|
265 IpEndpointName UdpSocket::LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const
|
rob@76
|
266 {
|
rob@76
|
267 return impl_->LocalEndpointFor( remoteEndpoint );
|
rob@76
|
268 }
|
rob@76
|
269
|
rob@76
|
270 void UdpSocket::Connect( const IpEndpointName& remoteEndpoint )
|
rob@76
|
271 {
|
rob@76
|
272 impl_->Connect( remoteEndpoint );
|
rob@76
|
273 }
|
rob@76
|
274
|
rob@76
|
275 void UdpSocket::Send( const char *data, std::size_t size )
|
rob@76
|
276 {
|
rob@76
|
277 impl_->Send( data, size );
|
rob@76
|
278 }
|
rob@76
|
279
|
rob@76
|
280 void UdpSocket::SendTo( const IpEndpointName& remoteEndpoint, const char *data, std::size_t size )
|
rob@76
|
281 {
|
rob@76
|
282 impl_->SendTo( remoteEndpoint, data, size );
|
rob@76
|
283 }
|
rob@76
|
284
|
rob@76
|
285 void UdpSocket::Bind( const IpEndpointName& localEndpoint )
|
rob@76
|
286 {
|
rob@76
|
287 impl_->Bind( localEndpoint );
|
rob@76
|
288 }
|
rob@76
|
289
|
rob@76
|
290 bool UdpSocket::IsBound() const
|
rob@76
|
291 {
|
rob@76
|
292 return impl_->IsBound();
|
rob@76
|
293 }
|
rob@76
|
294
|
rob@76
|
295 std::size_t UdpSocket::ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, std::size_t size )
|
rob@76
|
296 {
|
rob@76
|
297 return impl_->ReceiveFrom( remoteEndpoint, data, size );
|
rob@76
|
298 }
|
rob@76
|
299
|
rob@76
|
300
|
rob@76
|
301 struct AttachedTimerListener{
|
rob@76
|
302 AttachedTimerListener( int id, int p, TimerListener *tl )
|
rob@76
|
303 : initialDelayMs( id )
|
rob@76
|
304 , periodMs( p )
|
rob@76
|
305 , listener( tl ) {}
|
rob@76
|
306 int initialDelayMs;
|
rob@76
|
307 int periodMs;
|
rob@76
|
308 TimerListener *listener;
|
rob@76
|
309 };
|
rob@76
|
310
|
rob@76
|
311
|
rob@76
|
312 static bool CompareScheduledTimerCalls(
|
rob@76
|
313 const std::pair< double, AttachedTimerListener > & lhs, const std::pair< double, AttachedTimerListener > & rhs )
|
rob@76
|
314 {
|
rob@76
|
315 return lhs.first < rhs.first;
|
rob@76
|
316 }
|
rob@76
|
317
|
rob@76
|
318
|
rob@76
|
319 SocketReceiveMultiplexer *multiplexerInstanceToAbortWithSigInt_ = 0;
|
rob@76
|
320
|
rob@76
|
321 extern "C" /*static*/ void InterruptSignalHandler( int );
|
rob@76
|
322 /*static*/ void InterruptSignalHandler( int )
|
rob@76
|
323 {
|
rob@76
|
324 multiplexerInstanceToAbortWithSigInt_->AsynchronousBreak();
|
rob@76
|
325 #ifndef WINCE
|
rob@76
|
326 signal( SIGINT, SIG_DFL );
|
rob@76
|
327 #endif
|
rob@76
|
328 }
|
rob@76
|
329
|
rob@76
|
330
|
rob@76
|
331 class SocketReceiveMultiplexer::Implementation{
|
rob@76
|
332 NetworkInitializer networkInitializer_;
|
rob@76
|
333
|
rob@76
|
334 std::vector< std::pair< PacketListener*, UdpSocket* > > socketListeners_;
|
rob@76
|
335 std::vector< AttachedTimerListener > timerListeners_;
|
rob@76
|
336
|
rob@76
|
337 volatile bool break_;
|
rob@76
|
338 HANDLE breakEvent_;
|
rob@76
|
339
|
rob@76
|
340 double GetCurrentTimeMs() const
|
rob@76
|
341 {
|
rob@76
|
342 #ifndef WINCE
|
rob@76
|
343 return timeGetTime(); // FIXME: bad choice if you want to run for more than 40 days
|
rob@76
|
344 #else
|
rob@76
|
345 return 0;
|
rob@76
|
346 #endif
|
rob@76
|
347 }
|
rob@76
|
348
|
rob@76
|
349 public:
|
rob@76
|
350 Implementation()
|
rob@76
|
351 {
|
rob@76
|
352 breakEvent_ = CreateEvent( NULL, FALSE, FALSE, NULL );
|
rob@76
|
353 }
|
rob@76
|
354
|
rob@76
|
355 ~Implementation()
|
rob@76
|
356 {
|
rob@76
|
357 CloseHandle( breakEvent_ );
|
rob@76
|
358 }
|
rob@76
|
359
|
rob@76
|
360 void AttachSocketListener( UdpSocket *socket, PacketListener *listener )
|
rob@76
|
361 {
|
rob@76
|
362 assert( std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) ) == socketListeners_.end() );
|
rob@76
|
363 // we don't check that the same socket has been added multiple times, even though this is an error
|
rob@76
|
364 socketListeners_.push_back( std::make_pair( listener, socket ) );
|
rob@76
|
365 }
|
rob@76
|
366
|
rob@76
|
367 void DetachSocketListener( UdpSocket *socket, PacketListener *listener )
|
rob@76
|
368 {
|
rob@76
|
369 std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i =
|
rob@76
|
370 std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) );
|
rob@76
|
371 assert( i != socketListeners_.end() );
|
rob@76
|
372
|
rob@76
|
373 socketListeners_.erase( i );
|
rob@76
|
374 }
|
rob@76
|
375
|
rob@76
|
376 void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
|
rob@76
|
377 {
|
rob@76
|
378 timerListeners_.push_back( AttachedTimerListener( periodMilliseconds, periodMilliseconds, listener ) );
|
rob@76
|
379 }
|
rob@76
|
380
|
rob@76
|
381 void AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
|
rob@76
|
382 {
|
rob@76
|
383 timerListeners_.push_back( AttachedTimerListener( initialDelayMilliseconds, periodMilliseconds, listener ) );
|
rob@76
|
384 }
|
rob@76
|
385
|
rob@76
|
386 void DetachPeriodicTimerListener( TimerListener *listener )
|
rob@76
|
387 {
|
rob@76
|
388 std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
|
rob@76
|
389 while( i != timerListeners_.end() ){
|
rob@76
|
390 if( i->listener == listener )
|
rob@76
|
391 break;
|
rob@76
|
392 ++i;
|
rob@76
|
393 }
|
rob@76
|
394
|
rob@76
|
395 assert( i != timerListeners_.end() );
|
rob@76
|
396
|
rob@76
|
397 timerListeners_.erase( i );
|
rob@76
|
398 }
|
rob@76
|
399
|
rob@76
|
400 void Run()
|
rob@76
|
401 {
|
rob@76
|
402 break_ = false;
|
rob@76
|
403
|
rob@76
|
404 // prepare the window events which we use to wake up on incoming data
|
rob@76
|
405 // we use this instead of select() primarily to support the AsyncBreak()
|
rob@76
|
406 // mechanism.
|
rob@76
|
407
|
rob@76
|
408 std::vector<HANDLE> events( socketListeners_.size() + 1, 0 );
|
rob@76
|
409 int j=0;
|
rob@76
|
410 for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
|
rob@76
|
411 i != socketListeners_.end(); ++i, ++j ){
|
rob@76
|
412
|
rob@76
|
413 HANDLE event = CreateEvent( NULL, FALSE, FALSE, NULL );
|
rob@76
|
414 WSAEventSelect( i->second->impl_->Socket(), event, FD_READ ); // note that this makes the socket non-blocking which is why we can safely call RecieveFrom() on all sockets below
|
rob@76
|
415 events[j] = event;
|
rob@76
|
416 }
|
rob@76
|
417
|
rob@76
|
418
|
rob@76
|
419 events[ socketListeners_.size() ] = breakEvent_; // last event in the collection is the break event
|
rob@76
|
420
|
rob@76
|
421
|
rob@76
|
422 // configure the timer queue
|
rob@76
|
423 double currentTimeMs = GetCurrentTimeMs();
|
rob@76
|
424
|
rob@76
|
425 // expiry time ms, listener
|
rob@76
|
426 std::vector< std::pair< double, AttachedTimerListener > > timerQueue_;
|
rob@76
|
427 for( std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin();
|
rob@76
|
428 i != timerListeners_.end(); ++i )
|
rob@76
|
429 timerQueue_.push_back( std::make_pair( currentTimeMs + i->initialDelayMs, *i ) );
|
rob@76
|
430 std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
|
rob@76
|
431
|
rob@76
|
432 const int MAX_BUFFER_SIZE = 4098;
|
rob@76
|
433 char *data = new char[ MAX_BUFFER_SIZE ];
|
rob@76
|
434 IpEndpointName remoteEndpoint;
|
rob@76
|
435
|
rob@76
|
436 while( !break_ ){
|
rob@76
|
437
|
rob@76
|
438 double currentTimeMs = GetCurrentTimeMs();
|
rob@76
|
439
|
rob@76
|
440 DWORD waitTime = INFINITE;
|
rob@76
|
441 if( !timerQueue_.empty() ){
|
rob@76
|
442
|
rob@76
|
443 waitTime = (DWORD)( timerQueue_.front().first >= currentTimeMs
|
rob@76
|
444 ? timerQueue_.front().first - currentTimeMs
|
rob@76
|
445 : 0 );
|
rob@76
|
446 }
|
rob@76
|
447
|
rob@76
|
448 DWORD waitResult = WaitForMultipleObjects( (DWORD)socketListeners_.size() + 1, &events[0], FALSE, waitTime );
|
rob@76
|
449 if( break_ )
|
rob@76
|
450 break;
|
rob@76
|
451
|
rob@76
|
452 if( waitResult != WAIT_TIMEOUT ){
|
rob@76
|
453 for( int i = waitResult - WAIT_OBJECT_0; i < (int)socketListeners_.size(); ++i ){
|
rob@76
|
454 std::size_t size = socketListeners_[i].second->ReceiveFrom( remoteEndpoint, data, MAX_BUFFER_SIZE );
|
rob@76
|
455 if( size > 0 ){
|
rob@76
|
456 socketListeners_[i].first->ProcessPacket( data, (int)size, remoteEndpoint );
|
rob@76
|
457 if( break_ )
|
rob@76
|
458 break;
|
rob@76
|
459 }
|
rob@76
|
460 }
|
rob@76
|
461 }
|
rob@76
|
462
|
rob@76
|
463 // execute any expired timers
|
rob@76
|
464 currentTimeMs = GetCurrentTimeMs();
|
rob@76
|
465 bool resort = false;
|
rob@76
|
466 for( std::vector< std::pair< double, AttachedTimerListener > >::iterator i = timerQueue_.begin();
|
rob@76
|
467 i != timerQueue_.end() && i->first <= currentTimeMs; ++i ){
|
rob@76
|
468
|
rob@76
|
469 i->second.listener->TimerExpired();
|
rob@76
|
470 if( break_ )
|
rob@76
|
471 break;
|
rob@76
|
472
|
rob@76
|
473 i->first += i->second.periodMs;
|
rob@76
|
474 resort = true;
|
rob@76
|
475 }
|
rob@76
|
476 if( resort )
|
rob@76
|
477 std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls );
|
rob@76
|
478 }
|
rob@76
|
479
|
rob@76
|
480 delete [] data;
|
rob@76
|
481
|
rob@76
|
482 // free events
|
rob@76
|
483 j = 0;
|
rob@76
|
484 for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin();
|
rob@76
|
485 i != socketListeners_.end(); ++i, ++j ){
|
rob@76
|
486
|
rob@76
|
487 WSAEventSelect( i->second->impl_->Socket(), events[j], 0 ); // remove association between socket and event
|
rob@76
|
488 CloseHandle( events[j] );
|
rob@76
|
489 unsigned long enableNonblocking = 0;
|
rob@76
|
490 ioctlsocket( i->second->impl_->Socket(), FIONBIO, &enableNonblocking ); // make the socket blocking again
|
rob@76
|
491 }
|
rob@76
|
492 }
|
rob@76
|
493
|
rob@76
|
494 void Break()
|
rob@76
|
495 {
|
rob@76
|
496 break_ = true;
|
rob@76
|
497 }
|
rob@76
|
498
|
rob@76
|
499 void AsynchronousBreak()
|
rob@76
|
500 {
|
rob@76
|
501 break_ = true;
|
rob@76
|
502 SetEvent( breakEvent_ );
|
rob@76
|
503 }
|
rob@76
|
504 };
|
rob@76
|
505
|
rob@76
|
506
|
rob@76
|
507
|
rob@76
|
508 SocketReceiveMultiplexer::SocketReceiveMultiplexer()
|
rob@76
|
509 {
|
rob@76
|
510 impl_ = new Implementation();
|
rob@76
|
511 }
|
rob@76
|
512
|
rob@76
|
513 SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
|
rob@76
|
514 {
|
rob@76
|
515 delete impl_;
|
rob@76
|
516 }
|
rob@76
|
517
|
rob@76
|
518 void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket *socket, PacketListener *listener )
|
rob@76
|
519 {
|
rob@76
|
520 impl_->AttachSocketListener( socket, listener );
|
rob@76
|
521 }
|
rob@76
|
522
|
rob@76
|
523 void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket *socket, PacketListener *listener )
|
rob@76
|
524 {
|
rob@76
|
525 impl_->DetachSocketListener( socket, listener );
|
rob@76
|
526 }
|
rob@76
|
527
|
rob@76
|
528 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener )
|
rob@76
|
529 {
|
rob@76
|
530 impl_->AttachPeriodicTimerListener( periodMilliseconds, listener );
|
rob@76
|
531 }
|
rob@76
|
532
|
rob@76
|
533 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener )
|
rob@76
|
534 {
|
rob@76
|
535 impl_->AttachPeriodicTimerListener( initialDelayMilliseconds, periodMilliseconds, listener );
|
rob@76
|
536 }
|
rob@76
|
537
|
rob@76
|
538 void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener *listener )
|
rob@76
|
539 {
|
rob@76
|
540 impl_->DetachPeriodicTimerListener( listener );
|
rob@76
|
541 }
|
rob@76
|
542
|
rob@76
|
543 void SocketReceiveMultiplexer::Run()
|
rob@76
|
544 {
|
rob@76
|
545 impl_->Run();
|
rob@76
|
546 }
|
rob@76
|
547
|
rob@76
|
548 void SocketReceiveMultiplexer::RunUntilSigInt()
|
rob@76
|
549 {
|
rob@76
|
550 assert( multiplexerInstanceToAbortWithSigInt_ == 0 ); /* at present we support only one multiplexer instance running until sig int */
|
rob@76
|
551 multiplexerInstanceToAbortWithSigInt_ = this;
|
rob@76
|
552 #ifndef WINCE
|
rob@76
|
553 signal( SIGINT, InterruptSignalHandler );
|
rob@76
|
554 #endif
|
rob@76
|
555 impl_->Run();
|
rob@76
|
556 #ifndef WINCE
|
rob@76
|
557 signal( SIGINT, SIG_DFL );
|
rob@76
|
558 #endif
|
rob@76
|
559 multiplexerInstanceToAbortWithSigInt_ = 0;
|
rob@76
|
560 }
|
rob@76
|
561
|
rob@76
|
562 void SocketReceiveMultiplexer::Break()
|
rob@76
|
563 {
|
rob@76
|
564 impl_->Break();
|
rob@76
|
565 }
|
rob@76
|
566
|
rob@76
|
567 void SocketReceiveMultiplexer::AsynchronousBreak()
|
rob@76
|
568 {
|
rob@76
|
569 impl_->AsynchronousBreak();
|
rob@76
|
570 }
|
rob@76
|
571
|