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()
45 #include "ip/NetworkingUtils.h"
46 #include "ip/PacketListener.h"
47 #include "ip/TimerListener.h"
50 typedef int socklen_t
;
53 static void SockaddrFromIpEndpointName( struct sockaddr_in
& sockAddr
, const IpEndpointName
& endpoint
)
55 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
56 sockAddr
.sin_family
= AF_INET
;
58 sockAddr
.sin_addr
.s_addr
=
59 (endpoint
.address
== IpEndpointName::ANY_ADDRESS
)
61 : htonl( endpoint
.address
);
64 (endpoint
.port
== IpEndpointName::ANY_PORT
)
66 : htons( (short)endpoint
.port
);
70 static IpEndpointName
IpEndpointNameFromSockaddr( const struct sockaddr_in
& sockAddr
)
72 return IpEndpointName(
73 (sockAddr
.sin_addr
.s_addr
== INADDR_ANY
)
74 ? IpEndpointName::ANY_ADDRESS
75 : ntohl( sockAddr
.sin_addr
.s_addr
),
76 (sockAddr
.sin_port
== 0)
77 ? IpEndpointName::ANY_PORT
78 : ntohs( sockAddr
.sin_port
)
83 class UdpSocket::Implementation
{
84 NetworkInitializer networkInitializer_
;
90 struct sockaddr_in connectedAddr_
;
91 struct sockaddr_in sendToAddr_
;
97 , isConnected_( false )
98 , socket_( INVALID_SOCKET
)
100 if( (socket_
= socket( AF_INET
, SOCK_DGRAM
, 0 )) == INVALID_SOCKET
){
101 throw std::runtime_error("unable to create udp socket\n");
104 memset( &sendToAddr_
, 0, sizeof(sendToAddr_
) );
105 sendToAddr_
.sin_family
= AF_INET
;
110 if (socket_
!= INVALID_SOCKET
) closesocket(socket_
);
113 IpEndpointName
LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
117 // first connect the socket to the remote server
119 struct sockaddr_in connectSockAddr
;
120 SockaddrFromIpEndpointName( connectSockAddr
, remoteEndpoint
);
122 if (connect(socket_
, (struct sockaddr
*)&connectSockAddr
, sizeof(connectSockAddr
)) < 0) {
123 throw std::runtime_error("unable to connect udp socket\n");
128 struct sockaddr_in sockAddr
;
129 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
130 socklen_t length
= sizeof(sockAddr
);
131 if (getsockname(socket_
, (struct sockaddr
*)&sockAddr
, &length
) < 0) {
132 throw std::runtime_error("unable to getsockname\n");
136 // reconnect to the connected address
138 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
139 throw std::runtime_error("unable to connect udp socket\n");
143 // unconnect from the remote address
145 struct sockaddr_in unconnectSockAddr
;
146 SockaddrFromIpEndpointName( unconnectSockAddr
, IpEndpointName() );
148 if( connect(socket_
, (struct sockaddr
*)&unconnectSockAddr
, sizeof(unconnectSockAddr
)) < 0
149 && WSAGetLastError() != WSAEADDRNOTAVAIL
){
150 throw std::runtime_error("unable to un-connect udp socket\n");
154 return IpEndpointNameFromSockaddr( sockAddr
);
157 void Connect( const IpEndpointName
& remoteEndpoint
)
159 SockaddrFromIpEndpointName( connectedAddr_
, remoteEndpoint
);
161 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
162 throw std::runtime_error("unable to connect udp socket\n");
168 void Send( const char *data
, int size
)
170 assert( isConnected_
);
172 send( socket_
, data
, size
, 0 );
175 void SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
177 sendToAddr_
.sin_addr
.s_addr
= htonl( remoteEndpoint
.address
);
178 sendToAddr_
.sin_port
= htons( (short)remoteEndpoint
.port
);
180 sendto( socket_
, data
, size
, 0, (sockaddr
*)&sendToAddr_
, sizeof(sendToAddr_
) );
183 void Bind( const IpEndpointName
& localEndpoint
)
185 struct sockaddr_in bindSockAddr
;
186 SockaddrFromIpEndpointName( bindSockAddr
, localEndpoint
);
188 if (bind(socket_
, (struct sockaddr
*)&bindSockAddr
, sizeof(bindSockAddr
)) < 0) {
189 throw std::runtime_error("unable to bind udp socket\n");
195 bool IsBound() const { return isBound_
; }
197 int ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
201 struct sockaddr_in fromAddr
;
202 socklen_t fromAddrLen
= sizeof(fromAddr
);
204 int result
= recvfrom(socket_
, data
, size
, 0,
205 (struct sockaddr
*) &fromAddr
, (socklen_t
*)&fromAddrLen
);
209 remoteEndpoint
.address
= ntohl(fromAddr
.sin_addr
.s_addr
);
210 remoteEndpoint
.port
= ntohs(fromAddr
.sin_port
);
215 SOCKET
& Socket() { return socket_
; }
218 UdpSocket::UdpSocket()
220 impl_
= new Implementation();
223 UdpSocket::~UdpSocket()
228 IpEndpointName
UdpSocket::LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
230 return impl_
->LocalEndpointFor( remoteEndpoint
);
233 void UdpSocket::Connect( const IpEndpointName
& remoteEndpoint
)
235 impl_
->Connect( remoteEndpoint
);
238 void UdpSocket::Send( const char *data
, int size
)
240 impl_
->Send( data
, size
);
243 void UdpSocket::SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
245 impl_
->SendTo( remoteEndpoint
, data
, size
);
248 void UdpSocket::Bind( const IpEndpointName
& localEndpoint
)
250 impl_
->Bind( localEndpoint
);
253 bool UdpSocket::IsBound() const
255 return impl_
->IsBound();
258 int UdpSocket::ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
260 return impl_
->ReceiveFrom( remoteEndpoint
, data
, size
);
264 struct AttachedTimerListener
{
265 AttachedTimerListener( int id
, int p
, TimerListener
*tl
)
266 : initialDelayMs( id
)
271 TimerListener
*listener
;
275 static bool CompareScheduledTimerCalls(
276 const std::pair
< double, AttachedTimerListener
> & lhs
, const std::pair
< double, AttachedTimerListener
> & rhs
)
278 return lhs
.first
< rhs
.first
;
282 SocketReceiveMultiplexer
*multiplexerInstanceToAbortWithSigInt_
= 0;
284 extern "C" /*static*/ void InterruptSignalHandler( int );
285 /*static*/ void InterruptSignalHandler( int )
287 multiplexerInstanceToAbortWithSigInt_
->AsynchronousBreak();
289 signal( SIGINT
, SIG_DFL
);
294 class SocketReceiveMultiplexer::Implementation
{
295 NetworkInitializer networkInitializer_
;
297 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > > socketListeners_
;
298 std::vector
< AttachedTimerListener
> timerListeners_
;
300 volatile bool break_
;
303 double GetCurrentTimeMs() const
306 return timeGetTime(); // FIXME: bad choice if you want to run for more than 40 days
315 breakEvent_
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
320 CloseHandle( breakEvent_
);
323 void AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
325 assert( std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) ) == socketListeners_
.end() );
326 // we don't check that the same socket has been added multiple times, even though this is an error
327 socketListeners_
.push_back( std::make_pair( listener
, socket
) );
330 void DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
332 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
=
333 std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) );
334 assert( i
!= socketListeners_
.end() );
336 socketListeners_
.erase( i
);
339 void AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
341 timerListeners_
.push_back( AttachedTimerListener( periodMilliseconds
, periodMilliseconds
, listener
) );
344 void AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
346 timerListeners_
.push_back( AttachedTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
) );
349 void DetachPeriodicTimerListener( TimerListener
*listener
)
351 std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
352 while( i
!= timerListeners_
.end() ){
353 if( i
->listener
== listener
)
358 assert( i
!= timerListeners_
.end() );
360 timerListeners_
.erase( i
);
367 // prepare the window events which we use to wake up on incoming data
368 // we use this instead of select() primarily to support the AsyncBreak()
371 std::vector
<HANDLE
> events( socketListeners_
.size() + 1, 0 );
373 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
374 i
!= socketListeners_
.end(); ++i
, ++j
){
376 HANDLE event
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
377 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
382 events
[ socketListeners_
.size() ] = breakEvent_
; // last event in the collection is the break event
385 // configure the timer queue
386 double currentTimeMs
= GetCurrentTimeMs();
388 // expiry time ms, listener
389 std::vector
< std::pair
< double, AttachedTimerListener
> > timerQueue_
;
390 for( std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
391 i
!= timerListeners_
.end(); ++i
)
392 timerQueue_
.push_back( std::make_pair( currentTimeMs
+ i
->initialDelayMs
, *i
) );
393 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
395 const int MAX_BUFFER_SIZE
= 4098;
396 char *data
= new char[ MAX_BUFFER_SIZE
];
397 IpEndpointName remoteEndpoint
;
401 double currentTimeMs
= GetCurrentTimeMs();
403 DWORD waitTime
= INFINITE
;
404 if( !timerQueue_
.empty() ){
406 waitTime
= (DWORD
)( timerQueue_
.front().first
>= currentTimeMs
407 ? timerQueue_
.front().first
- currentTimeMs
411 DWORD waitResult
= WaitForMultipleObjects( (DWORD
)socketListeners_
.size() + 1, &events
[0], FALSE
, waitTime
);
415 if( waitResult
!= WAIT_TIMEOUT
){
416 for( int i
= waitResult
- WAIT_OBJECT_0
; i
< (int)socketListeners_
.size(); ++i
){
417 int size
= socketListeners_
[i
].second
->ReceiveFrom( remoteEndpoint
, data
, MAX_BUFFER_SIZE
);
419 socketListeners_
[i
].first
->ProcessPacket( data
, size
, remoteEndpoint
);
426 // execute any expired timers
427 currentTimeMs
= GetCurrentTimeMs();
429 for( std::vector
< std::pair
< double, AttachedTimerListener
> >::iterator i
= timerQueue_
.begin();
430 i
!= timerQueue_
.end() && i
->first
<= currentTimeMs
; ++i
){
432 i
->second
.listener
->TimerExpired();
436 i
->first
+= i
->second
.periodMs
;
440 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
447 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
448 i
!= socketListeners_
.end(); ++i
, ++j
){
450 WSAEventSelect( i
->second
->impl_
->Socket(), events
[j
], 0 ); // remove association between socket and event
451 CloseHandle( events
[j
] );
452 unsigned long enableNonblocking
= 0;
453 ioctlsocket( i
->second
->impl_
->Socket(), FIONBIO
, &enableNonblocking
); // make the socket blocking again
462 void AsynchronousBreak()
465 SetEvent( breakEvent_
);
471 SocketReceiveMultiplexer::SocketReceiveMultiplexer()
473 impl_
= new Implementation();
476 SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
481 void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
483 impl_
->AttachSocketListener( socket
, listener
);
486 void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
488 impl_
->DetachSocketListener( socket
, listener
);
491 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
493 impl_
->AttachPeriodicTimerListener( periodMilliseconds
, listener
);
496 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
498 impl_
->AttachPeriodicTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
);
501 void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener
*listener
)
503 impl_
->DetachPeriodicTimerListener( listener
);
506 void SocketReceiveMultiplexer::Run()
511 void SocketReceiveMultiplexer::RunUntilSigInt()
513 assert( multiplexerInstanceToAbortWithSigInt_
== 0 ); /* at present we support only one multiplexer instance running until sig int */
514 multiplexerInstanceToAbortWithSigInt_
= this;
516 signal( SIGINT
, InterruptSignalHandler
);
520 signal( SIGINT
, SIG_DFL
);
522 multiplexerInstanceToAbortWithSigInt_
= 0;
525 void SocketReceiveMultiplexer::Break()
530 void SocketReceiveMultiplexer::AsynchronousBreak()
532 impl_
->AsynchronousBreak();