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