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