2 oscpack -- Open Sound Control packet manipulation library
3 http://www.audiomulch.com/~rossb/oscpack
5 Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com>
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:
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
18 Any person wishing to distribute modifications to the Software is
19 requested to send the modifications to the original developer so that
20 they can be incorporated into the canonical version.
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #include "ip/UdpSocket.h"
32 #include <winsock2.h> // this must come first to prevent errors with MSVC7
34 #include <mmsystem.h> // for timeGetTime()
42 #include "ip/NetworkingUtils.h"
43 #include "ip/PacketListener.h"
44 #include "ip/TimerListener.h"
47 typedef int socklen_t
;
50 static void SockaddrFromIpEndpointName( struct sockaddr_in
& sockAddr
, const IpEndpointName
& endpoint
)
52 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
53 sockAddr
.sin_family
= AF_INET
;
55 sockAddr
.sin_addr
.s_addr
=
56 (endpoint
.address
== IpEndpointName::ANY_ADDRESS
)
58 : htonl( endpoint
.address
);
61 (endpoint
.port
== IpEndpointName::ANY_PORT
)
63 : htons( (short)endpoint
.port
);
67 static IpEndpointName
IpEndpointNameFromSockaddr( const struct sockaddr_in
& sockAddr
)
69 return IpEndpointName(
70 (sockAddr
.sin_addr
.s_addr
== INADDR_ANY
)
71 ? IpEndpointName::ANY_ADDRESS
72 : ntohl( sockAddr
.sin_addr
.s_addr
),
73 (sockAddr
.sin_port
== 0)
74 ? IpEndpointName::ANY_PORT
75 : ntohs( sockAddr
.sin_port
)
80 class UdpSocket::Implementation
{
81 NetworkInitializer networkInitializer_
;
87 struct sockaddr_in connectedAddr_
;
88 struct sockaddr_in sendToAddr_
;
94 , isConnected_( false )
95 , socket_( INVALID_SOCKET
)
97 if( (socket_
= socket( AF_INET
, SOCK_DGRAM
, 0 )) == INVALID_SOCKET
){
98 throw std::runtime_error("unable to create udp socket\n");
101 memset( &sendToAddr_
, 0, sizeof(sendToAddr_
) );
102 sendToAddr_
.sin_family
= AF_INET
;
107 if (socket_
!= INVALID_SOCKET
) closesocket(socket_
);
110 IpEndpointName
LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
114 // first connect the socket to the remote server
116 struct sockaddr_in connectSockAddr
;
117 SockaddrFromIpEndpointName( connectSockAddr
, remoteEndpoint
);
119 if (connect(socket_
, (struct sockaddr
*)&connectSockAddr
, sizeof(connectSockAddr
)) < 0) {
120 throw std::runtime_error("unable to connect udp socket\n");
125 struct sockaddr_in sockAddr
;
126 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
127 socklen_t length
= sizeof(sockAddr
);
128 if (getsockname(socket_
, (struct sockaddr
*)&sockAddr
, &length
) < 0) {
129 throw std::runtime_error("unable to getsockname\n");
133 // reconnect to the connected address
135 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
136 throw std::runtime_error("unable to connect udp socket\n");
140 // unconnect from the remote address
142 struct sockaddr_in unconnectSockAddr
;
143 SockaddrFromIpEndpointName( unconnectSockAddr
, IpEndpointName() );
145 if( connect(socket_
, (struct sockaddr
*)&unconnectSockAddr
, sizeof(unconnectSockAddr
)) < 0
146 && WSAGetLastError() != WSAEADDRNOTAVAIL
){
147 throw std::runtime_error("unable to un-connect udp socket\n");
151 return IpEndpointNameFromSockaddr( sockAddr
);
154 void Connect( const IpEndpointName
& remoteEndpoint
)
156 SockaddrFromIpEndpointName( connectedAddr_
, remoteEndpoint
);
158 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
159 throw std::runtime_error("unable to connect udp socket\n");
165 void Send( const char *data
, int size
)
167 assert( isConnected_
);
169 send( socket_
, data
, size
, 0 );
172 void SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
174 sendToAddr_
.sin_addr
.s_addr
= htonl( remoteEndpoint
.address
);
175 sendToAddr_
.sin_port
= htons( (short)remoteEndpoint
.port
);
177 sendto( socket_
, data
, size
, 0, (sockaddr
*)&sendToAddr_
, sizeof(sendToAddr_
) );
180 void Bind( const IpEndpointName
& localEndpoint
)
182 struct sockaddr_in bindSockAddr
;
183 SockaddrFromIpEndpointName( bindSockAddr
, localEndpoint
);
185 if (bind(socket_
, (struct sockaddr
*)&bindSockAddr
, sizeof(bindSockAddr
)) < 0) {
186 throw std::runtime_error("unable to bind udp socket\n");
192 bool IsBound() const { return isBound_
; }
194 int ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
198 struct sockaddr_in fromAddr
;
199 socklen_t fromAddrLen
= sizeof(fromAddr
);
201 int result
= recvfrom(socket_
, data
, size
, 0,
202 (struct sockaddr
*) &fromAddr
, (socklen_t
*)&fromAddrLen
);
206 remoteEndpoint
.address
= ntohl(fromAddr
.sin_addr
.s_addr
);
207 remoteEndpoint
.port
= ntohs(fromAddr
.sin_port
);
212 SOCKET
& Socket() { return socket_
; }
215 UdpSocket::UdpSocket()
217 impl_
= new Implementation();
220 UdpSocket::~UdpSocket()
225 IpEndpointName
UdpSocket::LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
227 return impl_
->LocalEndpointFor( remoteEndpoint
);
230 void UdpSocket::Connect( const IpEndpointName
& remoteEndpoint
)
232 impl_
->Connect( remoteEndpoint
);
235 void UdpSocket::Send( const char *data
, int size
)
237 impl_
->Send( data
, size
);
240 void UdpSocket::SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
242 impl_
->SendTo( remoteEndpoint
, data
, size
);
245 void UdpSocket::Bind( const IpEndpointName
& localEndpoint
)
247 impl_
->Bind( localEndpoint
);
250 bool UdpSocket::IsBound() const
252 return impl_
->IsBound();
255 int UdpSocket::ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
257 return impl_
->ReceiveFrom( remoteEndpoint
, data
, size
);
261 struct AttachedTimerListener
{
262 AttachedTimerListener( int id
, int p
, TimerListener
*tl
)
263 : initialDelayMs( id
)
268 TimerListener
*listener
;
272 static bool CompareScheduledTimerCalls(
273 const std::pair
< double, AttachedTimerListener
> & lhs
, const std::pair
< double, AttachedTimerListener
> & rhs
)
275 return lhs
.first
< rhs
.first
;
279 SocketReceiveMultiplexer
*multiplexerInstanceToAbortWithSigInt_
= 0;
281 extern "C" /*static*/ void InterruptSignalHandler( int );
282 /*static*/ void InterruptSignalHandler( int )
284 multiplexerInstanceToAbortWithSigInt_
->AsynchronousBreak();
285 signal( SIGINT
, SIG_DFL
);
289 class SocketReceiveMultiplexer::Implementation
{
290 NetworkInitializer networkInitializer_
;
292 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > > socketListeners_
;
293 std::vector
< AttachedTimerListener
> timerListeners_
;
295 volatile bool break_
;
298 double GetCurrentTimeMs() const
300 return timeGetTime(); // FIXME: bad choice if you want to run for more than 40 days
306 breakEvent_
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
311 CloseHandle( breakEvent_
);
314 void AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
316 assert( std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) ) == socketListeners_
.end() );
317 // we don't check that the same socket has been added multiple times, even though this is an error
318 socketListeners_
.push_back( std::make_pair( listener
, socket
) );
321 void DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
323 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
=
324 std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) );
325 assert( i
!= socketListeners_
.end() );
327 socketListeners_
.erase( i
);
330 void AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
332 timerListeners_
.push_back( AttachedTimerListener( periodMilliseconds
, periodMilliseconds
, listener
) );
335 void AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
337 timerListeners_
.push_back( AttachedTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
) );
340 void DetachPeriodicTimerListener( TimerListener
*listener
)
342 std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
343 while( i
!= timerListeners_
.end() ){
344 if( i
->listener
== listener
)
349 assert( i
!= timerListeners_
.end() );
351 timerListeners_
.erase( i
);
358 // prepare the window events which we use to wake up on incoming data
359 // we use this instead of select() primarily to support the AsyncBreak()
362 std::vector
<HANDLE
> events( socketListeners_
.size() + 1, 0 );
364 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
365 i
!= socketListeners_
.end(); ++i
, ++j
){
367 HANDLE event
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
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
373 events
[ socketListeners_
.size() ] = breakEvent_
; // last event in the collection is the break event
376 // configure the timer queue
377 double currentTimeMs
= GetCurrentTimeMs();
379 // expiry time ms, listener
380 std::vector
< std::pair
< double, AttachedTimerListener
> > timerQueue_
;
381 for( std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
382 i
!= timerListeners_
.end(); ++i
)
383 timerQueue_
.push_back( std::make_pair( currentTimeMs
+ i
->initialDelayMs
, *i
) );
384 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
386 const int MAX_BUFFER_SIZE
= 4098;
387 char *data
= new char[ MAX_BUFFER_SIZE
];
388 IpEndpointName remoteEndpoint
;
392 double currentTimeMs
= GetCurrentTimeMs();
394 DWORD waitTime
= INFINITE
;
395 if( !timerQueue_
.empty() ){
397 waitTime
= (DWORD
)( timerQueue_
.front().first
>= currentTimeMs
398 ? timerQueue_
.front().first
- currentTimeMs
402 DWORD waitResult
= WaitForMultipleObjects( (DWORD
)socketListeners_
.size() + 1, &events
[0], FALSE
, waitTime
);
406 if( waitResult
!= WAIT_TIMEOUT
){
407 for( int i
= waitResult
- WAIT_OBJECT_0
; i
< (int)socketListeners_
.size(); ++i
){
408 int size
= socketListeners_
[i
].second
->ReceiveFrom( remoteEndpoint
, data
, MAX_BUFFER_SIZE
);
410 socketListeners_
[i
].first
->ProcessPacket( data
, size
, remoteEndpoint
);
417 // execute any expired timers
418 currentTimeMs
= GetCurrentTimeMs();
420 for( std::vector
< std::pair
< double, AttachedTimerListener
> >::iterator i
= timerQueue_
.begin();
421 i
!= timerQueue_
.end() && i
->first
<= currentTimeMs
; ++i
){
423 i
->second
.listener
->TimerExpired();
427 i
->first
+= i
->second
.periodMs
;
431 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
438 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
439 i
!= socketListeners_
.end(); ++i
, ++j
){
441 WSAEventSelect( i
->second
->impl_
->Socket(), events
[j
], 0 ); // remove association between socket and event
442 CloseHandle( events
[j
] );
443 unsigned long enableNonblocking
= 0;
444 ioctlsocket( i
->second
->impl_
->Socket(), FIONBIO
, &enableNonblocking
); // make the socket blocking again
453 void AsynchronousBreak()
456 SetEvent( breakEvent_
);
462 SocketReceiveMultiplexer::SocketReceiveMultiplexer()
464 impl_
= new Implementation();
467 SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
472 void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
474 impl_
->AttachSocketListener( socket
, listener
);
477 void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
479 impl_
->DetachSocketListener( socket
, listener
);
482 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
484 impl_
->AttachPeriodicTimerListener( periodMilliseconds
, listener
);
487 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
489 impl_
->AttachPeriodicTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
);
492 void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener
*listener
)
494 impl_
->DetachPeriodicTimerListener( listener
);
497 void SocketReceiveMultiplexer::Run()
502 void SocketReceiveMultiplexer::RunUntilSigInt()
504 assert( multiplexerInstanceToAbortWithSigInt_
== 0 ); /* at present we support only one multiplexer instance running until sig int */
505 multiplexerInstanceToAbortWithSigInt_
= this;
506 signal( SIGINT
, InterruptSignalHandler
);
508 signal( SIGINT
, SIG_DFL
);
509 multiplexerInstanceToAbortWithSigInt_
= 0;
512 void SocketReceiveMultiplexer::Break()
517 void SocketReceiveMultiplexer::AsynchronousBreak()
519 impl_
->AsynchronousBreak();