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"
39 #include <string.h> // for memset
46 #include <sys/types.h>
47 #include <sys/socket.h>
49 #include <netinet/in.h> // for sockaddr_in
51 #include "ip/PacketListener.h"
52 #include "ip/TimerListener.h"
55 #if defined(__APPLE__) && !defined(_SOCKLEN_T)
56 // pre system 10.3 didn have socklen_t
57 typedef ssize_t socklen_t
;
61 static void SockaddrFromIpEndpointName( struct sockaddr_in
& sockAddr
, const IpEndpointName
& endpoint
)
63 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
64 sockAddr
.sin_family
= AF_INET
;
66 sockAddr
.sin_addr
.s_addr
=
67 (endpoint
.address
== IpEndpointName::ANY_ADDRESS
)
69 : htonl( endpoint
.address
);
72 (endpoint
.port
== IpEndpointName::ANY_PORT
)
74 : htons( endpoint
.port
);
78 static IpEndpointName
IpEndpointNameFromSockaddr( const struct sockaddr_in
& sockAddr
)
80 return IpEndpointName(
81 (sockAddr
.sin_addr
.s_addr
== INADDR_ANY
)
82 ? IpEndpointName::ANY_ADDRESS
83 : ntohl( sockAddr
.sin_addr
.s_addr
),
84 (sockAddr
.sin_port
== 0)
85 ? IpEndpointName::ANY_PORT
86 : ntohs( sockAddr
.sin_port
)
91 class UdpSocket::Implementation
{
96 struct sockaddr_in connectedAddr_
;
97 struct sockaddr_in sendToAddr_
;
103 , isConnected_( false )
106 if( (socket_
= socket( AF_INET
, SOCK_DGRAM
, 0 )) == -1 ){
107 throw std::runtime_error("unable to create udp socket\n");
110 memset( &sendToAddr_
, 0, sizeof(sendToAddr_
) );
111 sendToAddr_
.sin_family
= AF_INET
;
116 if (socket_
!= -1) close(socket_
);
119 IpEndpointName
LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
123 // first connect the socket to the remote server
125 struct sockaddr_in connectSockAddr
;
126 SockaddrFromIpEndpointName( connectSockAddr
, remoteEndpoint
);
128 if (connect(socket_
, (struct sockaddr
*)&connectSockAddr
, sizeof(connectSockAddr
)) < 0) {
129 throw std::runtime_error("unable to connect udp socket\n");
134 struct sockaddr_in sockAddr
;
135 memset( (char *)&sockAddr
, 0, sizeof(sockAddr
) );
136 socklen_t length
= sizeof(sockAddr
);
137 if (getsockname(socket_
, (struct sockaddr
*)&sockAddr
, &length
) < 0) {
138 throw std::runtime_error("unable to getsockname\n");
142 // reconnect to the connected address
144 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
145 throw std::runtime_error("unable to connect udp socket\n");
149 // unconnect from the remote address
151 struct sockaddr_in unconnectSockAddr
;
152 memset( (char *)&unconnectSockAddr
, 0, sizeof(unconnectSockAddr
) );
153 unconnectSockAddr
.sin_family
= AF_UNSPEC
;
154 // address fields are zero
155 int connectResult
= connect(socket_
, (struct sockaddr
*)&unconnectSockAddr
, sizeof(unconnectSockAddr
));
156 if ( connectResult
< 0 && errno
!= EAFNOSUPPORT
) {
157 throw std::runtime_error("unable to un-connect udp socket\n");
161 return IpEndpointNameFromSockaddr( sockAddr
);
164 void Connect( const IpEndpointName
& remoteEndpoint
)
166 SockaddrFromIpEndpointName( connectedAddr_
, remoteEndpoint
);
168 if (connect(socket_
, (struct sockaddr
*)&connectedAddr_
, sizeof(connectedAddr_
)) < 0) {
169 throw std::runtime_error("unable to connect udp socket\n");
175 void Send( const char *data
, int size
)
177 assert( isConnected_
);
179 send( socket_
, data
, size
, 0 );
182 void SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
184 sendToAddr_
.sin_addr
.s_addr
= htonl( remoteEndpoint
.address
);
185 sendToAddr_
.sin_port
= htons( remoteEndpoint
.port
);
187 sendto( socket_
, data
, size
, 0, (sockaddr
*)&sendToAddr_
, sizeof(sendToAddr_
) );
190 void Bind( const IpEndpointName
& localEndpoint
)
192 struct sockaddr_in bindSockAddr
;
193 SockaddrFromIpEndpointName( bindSockAddr
, localEndpoint
);
195 if (bind(socket_
, (struct sockaddr
*)&bindSockAddr
, sizeof(bindSockAddr
)) < 0) {
196 throw std::runtime_error("unable to bind udp socket\n");
202 bool IsBound() const { return isBound_
; }
204 int ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
208 struct sockaddr_in fromAddr
;
209 socklen_t fromAddrLen
= sizeof(fromAddr
);
211 int result
= recvfrom(socket_
, data
, size
, 0,
212 (struct sockaddr
*) &fromAddr
, (socklen_t
*)&fromAddrLen
);
216 remoteEndpoint
.address
= ntohl(fromAddr
.sin_addr
.s_addr
);
217 remoteEndpoint
.port
= ntohs(fromAddr
.sin_port
);
222 int Socket() { return socket_
; }
225 UdpSocket::UdpSocket()
227 impl_
= new Implementation();
230 UdpSocket::~UdpSocket()
235 IpEndpointName
UdpSocket::LocalEndpointFor( const IpEndpointName
& remoteEndpoint
) const
237 return impl_
->LocalEndpointFor( remoteEndpoint
);
240 void UdpSocket::Connect( const IpEndpointName
& remoteEndpoint
)
242 impl_
->Connect( remoteEndpoint
);
245 void UdpSocket::Send( const char *data
, int size
)
247 impl_
->Send( data
, size
);
250 void UdpSocket::SendTo( const IpEndpointName
& remoteEndpoint
, const char *data
, int size
)
252 impl_
->SendTo( remoteEndpoint
, data
, size
);
255 void UdpSocket::Bind( const IpEndpointName
& localEndpoint
)
257 impl_
->Bind( localEndpoint
);
260 bool UdpSocket::IsBound() const
262 return impl_
->IsBound();
265 int UdpSocket::ReceiveFrom( IpEndpointName
& remoteEndpoint
, char *data
, int size
)
267 return impl_
->ReceiveFrom( remoteEndpoint
, data
, size
);
271 struct AttachedTimerListener
{
272 AttachedTimerListener( int id
, int p
, TimerListener
*tl
)
273 : initialDelayMs( id
)
278 TimerListener
*listener
;
282 static bool CompareScheduledTimerCalls(
283 const std::pair
< double, AttachedTimerListener
> & lhs
, const std::pair
< double, AttachedTimerListener
> & rhs
)
285 return lhs
.first
< rhs
.first
;
289 SocketReceiveMultiplexer
*multiplexerInstanceToAbortWithSigInt_
= 0;
291 extern "C" /*static*/ void InterruptSignalHandler( int );
292 /*static*/ void InterruptSignalHandler( int )
294 multiplexerInstanceToAbortWithSigInt_
->AsynchronousBreak();
295 signal( SIGINT
, SIG_DFL
);
299 class SocketReceiveMultiplexer::Implementation
{
300 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > > socketListeners_
;
301 std::vector
< AttachedTimerListener
> timerListeners_
;
303 volatile bool break_
;
304 int breakPipe_
[2]; // [0] is the reader descriptor and [1] the writer
306 double GetCurrentTimeMs() const
310 gettimeofday( &t
, 0 );
312 return ((double)t
.tv_sec
*1000.) + ((double)t
.tv_usec
/ 1000.);
318 if( pipe(breakPipe_
) != 0 )
319 throw std::runtime_error( "creation of asynchronous break pipes failed\n" );
324 close( breakPipe_
[0] );
325 close( breakPipe_
[1] );
328 void AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
330 assert( std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) ) == socketListeners_
.end() );
331 // we don't check that the same socket has been added multiple times, even though this is an error
332 socketListeners_
.push_back( std::make_pair( listener
, socket
) );
335 void DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
337 std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
=
338 std::find( socketListeners_
.begin(), socketListeners_
.end(), std::make_pair(listener
, socket
) );
339 assert( i
!= socketListeners_
.end() );
341 socketListeners_
.erase( i
);
344 void AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
346 timerListeners_
.push_back( AttachedTimerListener( periodMilliseconds
, periodMilliseconds
, listener
) );
349 void AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
351 timerListeners_
.push_back( AttachedTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
) );
354 void DetachPeriodicTimerListener( TimerListener
*listener
)
356 std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
357 while( i
!= timerListeners_
.end() ){
358 if( i
->listener
== listener
)
363 assert( i
!= timerListeners_
.end() );
365 timerListeners_
.erase( i
);
372 // configure the master fd_set for select()
374 fd_set masterfds
, tempfds
;
375 FD_ZERO( &masterfds
);
378 // in addition to listening to the inbound sockets we
379 // also listen to the asynchronous break pipe, so that AsynchronousBreak()
380 // can break us out of select() from another thread.
381 FD_SET( breakPipe_
[0], &masterfds
);
382 int fdmax
= breakPipe_
[0];
384 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
385 i
!= socketListeners_
.end(); ++i
){
387 if( fdmax
< i
->second
->impl_
->Socket() )
388 fdmax
= i
->second
->impl_
->Socket();
389 FD_SET( i
->second
->impl_
->Socket(), &masterfds
);
393 // configure the timer queue
394 double currentTimeMs
= GetCurrentTimeMs();
396 // expiry time ms, listener
397 std::vector
< std::pair
< double, AttachedTimerListener
> > timerQueue_
;
398 for( std::vector
< AttachedTimerListener
>::iterator i
= timerListeners_
.begin();
399 i
!= timerListeners_
.end(); ++i
)
400 timerQueue_
.push_back( std::make_pair( currentTimeMs
+ i
->initialDelayMs
, *i
) );
401 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
403 const int MAX_BUFFER_SIZE
= 4098;
404 char *data
= new char[ MAX_BUFFER_SIZE
];
405 IpEndpointName remoteEndpoint
;
407 struct timeval timeout
;
412 struct timeval
*timeoutPtr
= 0;
413 if( !timerQueue_
.empty() ){
414 double timeoutMs
= timerQueue_
.front().first
- GetCurrentTimeMs();
418 // 1000000 microseconds in a second
419 timeout
.tv_sec
= (long)(timeoutMs
* .001);
420 timeout
.tv_usec
= (long)((timeoutMs
- (timeout
.tv_sec
* 1000)) * 1000);
421 timeoutPtr
= &timeout
;
424 if( select( fdmax
+ 1, &tempfds
, 0, 0, timeoutPtr
) < 0 && errno
!= EINTR
){
425 throw std::runtime_error("select failed\n");
428 if ( FD_ISSET( breakPipe_
[0], &tempfds
) ){
429 // clear pending data from the asynchronous break pipe
431 read( breakPipe_
[0], &c
, 1 );
437 for( std::vector
< std::pair
< PacketListener
*, UdpSocket
* > >::iterator i
= socketListeners_
.begin();
438 i
!= socketListeners_
.end(); ++i
){
440 if( FD_ISSET( i
->second
->impl_
->Socket(), &tempfds
) ){
442 int size
= i
->second
->ReceiveFrom( remoteEndpoint
, data
, MAX_BUFFER_SIZE
);
444 i
->first
->ProcessPacket( data
, size
, remoteEndpoint
);
451 // execute any expired timers
452 currentTimeMs
= GetCurrentTimeMs();
454 for( std::vector
< std::pair
< double, AttachedTimerListener
> >::iterator i
= timerQueue_
.begin();
455 i
!= timerQueue_
.end() && i
->first
<= currentTimeMs
; ++i
){
457 i
->second
.listener
->TimerExpired();
461 i
->first
+= i
->second
.periodMs
;
465 std::sort( timerQueue_
.begin(), timerQueue_
.end(), CompareScheduledTimerCalls
);
476 void AsynchronousBreak()
480 // Send a termination message to the asynchronous break pipe, so select() will return
481 write( breakPipe_
[1], "!", 1 );
487 SocketReceiveMultiplexer::SocketReceiveMultiplexer()
489 impl_
= new Implementation();
492 SocketReceiveMultiplexer::~SocketReceiveMultiplexer()
497 void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
499 impl_
->AttachSocketListener( socket
, listener
);
502 void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket
*socket
, PacketListener
*listener
)
504 impl_
->DetachSocketListener( socket
, listener
);
507 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds
, TimerListener
*listener
)
509 impl_
->AttachPeriodicTimerListener( periodMilliseconds
, listener
);
512 void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds
, int periodMilliseconds
, TimerListener
*listener
)
514 impl_
->AttachPeriodicTimerListener( initialDelayMilliseconds
, periodMilliseconds
, listener
);
517 void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener
*listener
)
519 impl_
->DetachPeriodicTimerListener( listener
);
522 void SocketReceiveMultiplexer::Run()
527 void SocketReceiveMultiplexer::RunUntilSigInt()
529 assert( multiplexerInstanceToAbortWithSigInt_
== 0 ); /* at present we support only one multiplexer instance running until sig int */
530 multiplexerInstanceToAbortWithSigInt_
= this;
531 signal( SIGINT
, InterruptSignalHandler
);
533 signal( SIGINT
, SIG_DFL
);
534 multiplexerInstanceToAbortWithSigInt_
= 0;
537 void SocketReceiveMultiplexer::Break()
542 void SocketReceiveMultiplexer::AsynchronousBreak()
544 impl_
->AsynchronousBreak();