2016-11-04 08:31:05 +00:00
using System ;
2016-10-29 22:22:20 +00:00
using System.Collections.Generic ;
using System.Linq ;
using System.Net ;
using System.Net.Sockets ;
using System.Security ;
using System.Threading.Tasks ;
2016-11-04 08:31:05 +00:00
using MediaBrowser.Model.Net ;
2016-10-29 22:22:20 +00:00
2016-11-04 08:31:05 +00:00
namespace Emby.Common.Implementations.Net
2016-10-29 22:22:20 +00:00
{
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
// Be careful to check any changes compile and work for all platform projects it is shared in.
internal sealed class UdpSocket : DisposableManagedObjectBase , IUdpSocket
{
#region Fields
private System . Net . Sockets . Socket _Socket ;
private int _LocalPort ;
#endregion
#region Constructors
2016-11-04 18:56:47 +00:00
public UdpSocket ( System . Net . Sockets . Socket socket , int localPort , IPAddress ip )
2016-10-29 22:22:20 +00:00
{
if ( socket = = null ) throw new ArgumentNullException ( "socket" ) ;
_Socket = socket ;
_LocalPort = localPort ;
_Socket . Bind ( new IPEndPoint ( ip , _LocalPort ) ) ;
if ( _LocalPort = = 0 )
_LocalPort = ( _Socket . LocalEndPoint as IPEndPoint ) . Port ;
}
#endregion
#region IUdpSocket Members
2016-11-04 18:56:47 +00:00
public Task < SocketReceiveResult > ReceiveAsync ( )
2016-10-29 22:22:20 +00:00
{
ThrowIfDisposed ( ) ;
2016-11-04 18:56:47 +00:00
var tcs = new TaskCompletionSource < SocketReceiveResult > ( ) ;
2016-10-29 22:22:20 +00:00
System . Net . EndPoint receivedFromEndPoint = new IPEndPoint ( IPAddress . Any , 0 ) ;
var state = new AsyncReceiveState ( _Socket , receivedFromEndPoint ) ;
state . TaskCompletionSource = tcs ;
#if NETSTANDARD1_6
_Socket . ReceiveFromAsync ( new System . ArraySegment < Byte > ( state . Buffer ) , System . Net . Sockets . SocketFlags . None , state . EndPoint )
. ContinueWith ( ( task , asyncState ) = >
{
if ( task . Status ! = TaskStatus . Faulted )
{
var receiveState = asyncState as AsyncReceiveState ;
receiveState . EndPoint = task . Result . RemoteEndPoint ;
ProcessResponse ( receiveState , ( ) = > task . Result . ReceivedBytes ) ;
}
} , state ) ;
#else
_Socket . BeginReceiveFrom ( state . Buffer , 0 , state . Buffer . Length , System . Net . Sockets . SocketFlags . None , ref state . EndPoint , new AsyncCallback ( this . ProcessResponse ) , state ) ;
#endif
return tcs . Task ;
}
2016-11-04 18:56:47 +00:00
public Task SendAsync ( byte [ ] buffer , int size , IpEndPointInfo endPoint )
2016-10-29 22:22:20 +00:00
{
ThrowIfDisposed ( ) ;
2016-11-04 18:56:47 +00:00
if ( buffer = = null ) throw new ArgumentNullException ( "messageData" ) ;
2016-10-29 22:22:20 +00:00
if ( endPoint = = null ) throw new ArgumentNullException ( "endPoint" ) ;
#if NETSTANDARD1_6
2016-11-04 18:56:47 +00:00
if ( size ! = buffer . Length )
{
byte [ ] copy = new byte [ size ] ;
Buffer . BlockCopy ( buffer , 0 , copy , 0 , size ) ;
buffer = copy ;
}
_Socket . SendTo ( buffer , new System . Net . IPEndPoint ( IPAddress . Parse ( endPoint . IpAddress . ToString ( ) ) , endPoint . Port ) ) ;
2016-10-29 22:22:20 +00:00
return Task . FromResult ( true ) ;
#else
var taskSource = new TaskCompletionSource < bool > ( ) ;
try
{
2016-11-04 18:56:47 +00:00
_Socket . BeginSendTo ( buffer , 0 , size , SocketFlags . None , new System . Net . IPEndPoint ( IPAddress . Parse ( endPoint . IpAddress . ToString ( ) ) , endPoint . Port ) , result = >
2016-10-29 22:22:20 +00:00
{
try
{
_Socket . EndSend ( result ) ;
taskSource . TrySetResult ( true ) ;
}
catch ( SocketException ex )
{
taskSource . TrySetException ( ex ) ;
}
catch ( ObjectDisposedException ex )
{
taskSource . TrySetException ( ex ) ;
}
catch ( InvalidOperationException ex )
{
taskSource . TrySetException ( ex ) ;
}
catch ( SecurityException ex )
{
taskSource . TrySetException ( ex ) ;
}
} , null ) ;
}
catch ( SocketException ex )
{
taskSource . TrySetException ( ex ) ;
}
catch ( ObjectDisposedException ex )
{
taskSource . TrySetException ( ex ) ;
}
catch ( SecurityException ex )
{
taskSource . TrySetException ( ex ) ;
}
//_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IPAddress), endPoint.Port));
return taskSource . Task ;
#endif
}
#endregion
#region Overrides
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
var socket = _Socket ;
if ( socket ! = null )
socket . Dispose ( ) ;
}
}
#endregion
#region Private Methods
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")]
private static void ProcessResponse ( AsyncReceiveState state , Func < int > receiveData )
{
try
{
var bytesRead = receiveData ( ) ;
var ipEndPoint = state . EndPoint as IPEndPoint ;
state . TaskCompletionSource . SetResult (
2016-11-04 18:56:47 +00:00
new SocketReceiveResult ( )
2016-10-29 22:22:20 +00:00
{
Buffer = state . Buffer ,
ReceivedBytes = bytesRead ,
2016-11-04 18:56:47 +00:00
RemoteEndPoint = ToIpEndPointInfo ( ipEndPoint )
2016-10-29 22:22:20 +00:00
}
) ;
}
catch ( ObjectDisposedException )
{
state . TaskCompletionSource . SetCanceled ( ) ;
}
catch ( SocketException se )
{
if ( se . SocketErrorCode ! = SocketError . Interrupted & & se . SocketErrorCode ! = SocketError . OperationAborted & & se . SocketErrorCode ! = SocketError . Shutdown )
state . TaskCompletionSource . SetException ( se ) ;
else
state . TaskCompletionSource . SetCanceled ( ) ;
}
catch ( Exception ex )
{
state . TaskCompletionSource . SetException ( ex ) ;
}
}
2016-11-04 08:31:05 +00:00
private static IpEndPointInfo ToIpEndPointInfo ( IPEndPoint endpoint )
{
if ( endpoint = = null )
{
return null ;
}
return new IpEndPointInfo
{
IpAddress = new IpAddressInfo
{
Address = endpoint . Address . ToString ( ) ,
IsIpv6 = endpoint . AddressFamily = = AddressFamily . InterNetworkV6
} ,
Port = endpoint . Port
} ;
}
2016-10-29 22:22:20 +00:00
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")]
private void ProcessResponse ( IAsyncResult asyncResult )
{
#if NET46
var state = asyncResult . AsyncState as AsyncReceiveState ;
try
{
var bytesRead = state . Socket . EndReceiveFrom ( asyncResult , ref state . EndPoint ) ;
var ipEndPoint = state . EndPoint as IPEndPoint ;
state . TaskCompletionSource . SetResult (
2016-11-04 18:56:47 +00:00
new SocketReceiveResult
2016-10-29 22:22:20 +00:00
{
Buffer = state . Buffer ,
ReceivedBytes = bytesRead ,
2016-11-04 18:56:47 +00:00
RemoteEndPoint = ToIpEndPointInfo ( ipEndPoint )
2016-10-29 22:22:20 +00:00
}
) ;
}
catch ( ObjectDisposedException )
{
state . TaskCompletionSource . SetCanceled ( ) ;
}
catch ( SocketException se )
{
if ( se . SocketErrorCode ! = SocketError . Interrupted & & se . SocketErrorCode ! = SocketError . OperationAborted & & se . SocketErrorCode ! = SocketError . Shutdown )
state . TaskCompletionSource . SetException ( se ) ;
else
state . TaskCompletionSource . SetCanceled ( ) ;
}
catch ( Exception ex )
{
state . TaskCompletionSource . SetException ( ex ) ;
}
#endif
}
#endregion
#region Private Classes
private class AsyncReceiveState
{
public AsyncReceiveState ( System . Net . Sockets . Socket socket , EndPoint endPoint )
{
this . Socket = socket ;
this . EndPoint = endPoint ;
}
public EndPoint EndPoint ;
2016-11-04 08:31:05 +00:00
public byte [ ] Buffer = new byte [ 8192 ] ;
2016-10-29 22:22:20 +00:00
public System . Net . Sockets . Socket Socket { get ; private set ; }
2016-11-04 18:56:47 +00:00
public TaskCompletionSource < SocketReceiveResult > TaskCompletionSource { get ; set ; }
2016-10-29 22:22:20 +00:00
}
#endregion
}
2016-11-04 08:31:05 +00:00
}