parent
3a91c37283
commit
42498194d9
@ -1,267 +0,0 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace Emby.Server.Implementations.Net
|
||||
{
|
||||
// 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.
|
||||
|
||||
public sealed class UdpSocket : ISocket, IDisposable
|
||||
{
|
||||
private readonly int _localPort;
|
||||
|
||||
private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
|
||||
{
|
||||
SocketFlags = SocketFlags.None
|
||||
};
|
||||
|
||||
private readonly SocketAsyncEventArgs _sendSocketAsyncEventArgs = new SocketAsyncEventArgs()
|
||||
{
|
||||
SocketFlags = SocketFlags.None
|
||||
};
|
||||
|
||||
private Socket _socket;
|
||||
private bool _disposed = false;
|
||||
private TaskCompletionSource<SocketReceiveResult> _currentReceiveTaskCompletionSource;
|
||||
private TaskCompletionSource<int> _currentSendTaskCompletionSource;
|
||||
|
||||
public UdpSocket(Socket socket, int localPort, IPAddress ip)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(socket);
|
||||
|
||||
_socket = socket;
|
||||
_localPort = localPort;
|
||||
LocalIPAddress = ip;
|
||||
|
||||
_socket.Bind(new IPEndPoint(ip, _localPort));
|
||||
|
||||
InitReceiveSocketAsyncEventArgs();
|
||||
}
|
||||
|
||||
public UdpSocket(Socket socket, IPEndPoint endPoint)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(socket);
|
||||
|
||||
_socket = socket;
|
||||
_socket.Connect(endPoint);
|
||||
|
||||
InitReceiveSocketAsyncEventArgs();
|
||||
}
|
||||
|
||||
public Socket Socket => _socket;
|
||||
|
||||
public IPAddress LocalIPAddress { get; }
|
||||
|
||||
private void InitReceiveSocketAsyncEventArgs()
|
||||
{
|
||||
var receiveBuffer = new byte[8192];
|
||||
_receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
|
||||
_receiveSocketAsyncEventArgs.Completed += OnReceiveSocketAsyncEventArgsCompleted;
|
||||
|
||||
var sendBuffer = new byte[8192];
|
||||
_sendSocketAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
|
||||
_sendSocketAsyncEventArgs.Completed += OnSendSocketAsyncEventArgsCompleted;
|
||||
}
|
||||
|
||||
private void OnReceiveSocketAsyncEventArgsCompleted(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
var tcs = _currentReceiveTaskCompletionSource;
|
||||
if (tcs is not null)
|
||||
{
|
||||
_currentReceiveTaskCompletionSource = null;
|
||||
|
||||
if (e.SocketError == SocketError.Success)
|
||||
{
|
||||
tcs.TrySetResult(new SocketReceiveResult
|
||||
{
|
||||
Buffer = e.Buffer,
|
||||
ReceivedBytes = e.BytesTransferred,
|
||||
RemoteEndPoint = e.RemoteEndPoint as IPEndPoint,
|
||||
LocalIPAddress = LocalIPAddress
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.TrySetException(new SocketException((int)e.SocketError));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSendSocketAsyncEventArgsCompleted(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
var tcs = _currentSendTaskCompletionSource;
|
||||
if (tcs is not null)
|
||||
{
|
||||
_currentSendTaskCompletionSource = null;
|
||||
|
||||
if (e.SocketError == SocketError.Success)
|
||||
{
|
||||
tcs.TrySetResult(e.BytesTransferred);
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.TrySetException(new SocketException((int)e.SocketError));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
return _socket.BeginReceiveFrom(buffer, offset, count, SocketFlags.None, ref receivedFromEndPoint, callback, buffer);
|
||||
}
|
||||
|
||||
public int Receive(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return _socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
|
||||
}
|
||||
|
||||
public SocketReceiveResult EndReceive(IAsyncResult result)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var sender = new IPEndPoint(IPAddress.Any, 0);
|
||||
var remoteEndPoint = (EndPoint)sender;
|
||||
|
||||
var receivedBytes = _socket.EndReceiveFrom(result, ref remoteEndPoint);
|
||||
|
||||
var buffer = (byte[])result.AsyncState;
|
||||
|
||||
return new SocketReceiveResult
|
||||
{
|
||||
ReceivedBytes = receivedBytes,
|
||||
RemoteEndPoint = (IPEndPoint)remoteEndPoint,
|
||||
Buffer = buffer,
|
||||
LocalIPAddress = LocalIPAddress
|
||||
};
|
||||
}
|
||||
|
||||
public Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var taskCompletion = new TaskCompletionSource<SocketReceiveResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
bool isResultSet = false;
|
||||
|
||||
Action<IAsyncResult> callback = callbackResult =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isResultSet)
|
||||
{
|
||||
isResultSet = true;
|
||||
taskCompletion.TrySetResult(EndReceive(callbackResult));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskCompletion.TrySetException(ex);
|
||||
}
|
||||
};
|
||||
|
||||
var result = BeginReceive(buffer, offset, count, new AsyncCallback(callback));
|
||||
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
callback(result);
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
||||
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
|
||||
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
||||
public Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var taskCompletion = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
bool isResultSet = false;
|
||||
|
||||
Action<IAsyncResult> callback = callbackResult =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isResultSet)
|
||||
{
|
||||
isResultSet = true;
|
||||
taskCompletion.TrySetResult(EndSendTo(callbackResult));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskCompletion.TrySetException(ex);
|
||||
}
|
||||
};
|
||||
|
||||
var result = BeginSendTo(buffer, offset, bytes, endPoint, new AsyncCallback(callback), null);
|
||||
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
callback(result);
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
||||
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
|
||||
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
||||
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IPEndPoint endPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, endPoint, callback, state);
|
||||
}
|
||||
|
||||
public int EndSendTo(IAsyncResult result)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return _socket.EndSendTo(result);
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(UdpSocket));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_socket?.Dispose();
|
||||
_receiveSocketAsyncEventArgs.Dispose();
|
||||
_sendSocketAsyncEventArgs.Dispose();
|
||||
_currentReceiveTaskCompletionSource?.TrySetCanceled();
|
||||
_currentSendTaskCompletionSource?.TrySetCanceled();
|
||||
|
||||
_socket = null;
|
||||
_currentReceiveTaskCompletionSource = null;
|
||||
_currentSendTaskCompletionSource = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Model.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a common interface across platforms for UDP sockets used by this SSDP implementation.
|
||||
/// </summary>
|
||||
public interface ISocket : IDisposable
|
||||
{
|
||||
IPAddress LocalIPAddress { get; }
|
||||
|
||||
Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||
|
||||
IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback);
|
||||
|
||||
SocketReceiveResult EndReceive(IAsyncResult result);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a UDP message to a particular end point (uni or multicast).
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of type <see cref="byte" /> that contains the data to send.</param>
|
||||
/// <param name="offset">The zero-based position in buffer at which to begin sending data.</param>
|
||||
/// <param name="bytes">The number of bytes to send.</param>
|
||||
/// <param name="endPoint">An <see cref="IPEndPoint" /> that represents the remote device.</param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
@ -1,32 +1,36 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MediaBrowser.Model.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="ISocket"/> interface.
|
||||
/// Implemented by components that can create specific socket configurations.
|
||||
/// </summary>
|
||||
public interface ISocketFactory
|
||||
{
|
||||
ISocket CreateUdpBroadcastSocket(int localPort);
|
||||
/// <summary>
|
||||
/// Creates a new unicast socket using the specified local port number.
|
||||
/// </summary>
|
||||
/// <param name="localPort">The local port to bind to.</param>
|
||||
/// <returns>A new unicast socket using the specified local port number.</returns>
|
||||
Socket CreateUdpBroadcastSocket(int localPort);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new unicast socket using the specified local port number.
|
||||
/// </summary>
|
||||
/// <param name="localIp">The local IP address to bind to.</param>
|
||||
/// <param name="bindInterface">The bind interface.</param>
|
||||
/// <param name="localPort">The local port to bind to.</param>
|
||||
/// <returns>A new unicast socket using the specified local port number.</returns>
|
||||
ISocket CreateSsdpUdpSocket(IPAddress localIp, int localPort);
|
||||
Socket CreateSsdpUdpSocket(IPData bindInterface, int localPort);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new multicast socket using the specified multicast IP address, multicast time to live and local port.
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">The multicast IP address to bind to.</param>
|
||||
/// <param name="bindIpAddress">The bind IP address.</param>
|
||||
/// <param name="multicastAddress">The multicast IP address to bind to.</param>
|
||||
/// <param name="bindInterface">The bind interface.</param>
|
||||
/// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param>
|
||||
/// <param name="localPort">The local port to bind to.</param>
|
||||
/// <returns>A <see cref="ISocket"/> implementation.</returns>
|
||||
ISocket CreateUdpMulticastSocket(IPAddress ipAddress, IPAddress bindIpAddress, int multicastTimeToLive, int localPort);
|
||||
/// <returns>A new multicast socket using the specfied bind interface, multicast address, multicast time to live and port.</returns>
|
||||
Socket CreateUdpMulticastSocket(IPAddress multicastAddress, IPData bindInterface, int multicastTimeToLive, int localPort);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue