From a660aa001eb31e91d040e066787fa764cf5f0fb4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 12 Mar 2017 15:27:26 -0400 Subject: re-organize file streaming --- MediaBrowser.Model/Net/IAcceptSocket.cs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'MediaBrowser.Model/Net') diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs index cac23b337..4262e2390 100644 --- a/MediaBrowser.Model/Net/IAcceptSocket.cs +++ b/MediaBrowser.Model/Net/IAcceptSocket.cs @@ -1,4 +1,6 @@ using System; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Model.Net { @@ -13,6 +15,7 @@ namespace MediaBrowser.Model.Net void Bind(IpEndPointInfo endpoint); void Connect(IpEndPointInfo endPoint); void StartAccept(Action onAccept, Func isClosed); + Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken); } public class SocketCreateException : Exception -- cgit v1.2.3 From b38b7a706268fe5d92d8cbe703a188b58ed7ec4d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:08:23 -0400 Subject: rework filestream --- Emby.Common.Implementations/Net/NetAcceptSocket.cs | 2 +- Emby.Common.Implementations/Net/SocketFactory.cs | 27 +++++++++++++++++++--- .../HttpServer/FileWriter.cs | 7 ++++-- .../HttpServer/HttpResultFactory.cs | 5 ++-- .../SocketSharp/WebSocketSharpResponse.cs | 4 ++-- .../Library/LibraryManager.cs | 27 ++++++++++++++-------- MediaBrowser.Api/StartupWizardService.cs | 1 + .../Entities/Audio/MusicArtist.cs | 7 +++++- .../Entities/Audio/MusicGenre.cs | 7 +++++- MediaBrowser.Controller/Entities/GameGenre.cs | 7 +++++- MediaBrowser.Controller/Entities/Genre.cs | 7 +++++- MediaBrowser.Controller/Entities/Person.cs | 7 +++++- MediaBrowser.Controller/Entities/Studio.cs | 7 +++++- MediaBrowser.Controller/Entities/Year.cs | 7 +++++- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 ++ .../Configuration/ServerConfiguration.cs | 1 + MediaBrowser.Model/Net/ISocketFactory.cs | 2 ++ MediaBrowser.Model/Services/IRequest.cs | 3 ++- .../Net/HttpListenerResponse.cs | 4 ++-- SocketHttpListener.Portable/Net/ResponseStream.cs | 8 +++---- 20 files changed, 108 insertions(+), 34 deletions(-) (limited to 'MediaBrowser.Model/Net') diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index e21ffe553..3721709e6 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -100,7 +100,7 @@ namespace Emby.Common.Implementations.Net #if NET46 public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) { - var options = TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket | TransmitFileOptions.UseKernelApc; + var options = TransmitFileOptions.UseKernelApc; var completionSource = new TaskCompletionSource(); diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index 021613e57..0f4306a6b 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -97,10 +97,31 @@ namespace Emby.Common.Implementations.Net } } + public ISocket CreateUdpBroadcastSocket(int localPort) + { + if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); + + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); + try + { + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + + return new UdpSocket(retVal, localPort, IPAddress.Any); + } + catch + { + if (retVal != null) + retVal.Dispose(); + + throw; + } + } + /// - /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. - /// - /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. + /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. + /// + /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index b80a40962..d230a9b91 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -27,6 +27,8 @@ namespace Emby.Server.Implementations.HttpServer private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); public List Cookies { get; private set; } + public FileShareMode FileShare { get; set; } + /// /// The _options /// @@ -69,6 +71,7 @@ namespace Emby.Server.Implementations.HttpServer SetRangeValues(); } + FileShare = FileShareMode.Read; Cookies = new List(); } @@ -153,11 +156,11 @@ namespace Emby.Server.Implementations.HttpServer if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1)) { Logger.Info("Transmit file {0}", Path); - await response.TransmitFile(Path, 0, 0, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false); return; } - await response.TransmitFile(Path, RangeStart, RangeEnd, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, RangeStart, RangeEnd, FileShare, cancellationToken).ConfigureAwait(false); } finally { diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index e3f105941..310161d41 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -556,12 +556,13 @@ namespace Emby.Server.Implementations.HttpServer { var rangeHeader = requestContext.Headers.Get("Range"); - if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path) && options.FileShare == FileShareMode.Read) + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) { OnComplete = options.OnComplete, - OnError = options.OnError + OnError = options.OnError, + FileShare = options.FileShare }; } diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs index a497ee715..fd30b227f 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp { } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return _response.TransmitFile(path, offset, count, cancellationToken); + return _response.TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f7706db47..026486efc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -513,6 +513,11 @@ namespace Emby.Server.Implementations.Library } public Guid GetNewItemId(string key, Type type) + { + return GetNewItemIdInternal(key, type, false); + } + + private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive) { if (string.IsNullOrWhiteSpace(key)) { @@ -531,7 +536,7 @@ namespace Emby.Server.Implementations.Library .Replace("/", "\\"); } - if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) + if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) { key = key.ToLower(); } @@ -865,7 +870,7 @@ namespace Emby.Server.Implementations.Library /// Task{Person}. public Person GetPerson(string name) { - return CreateItemByName(Person.GetPath(name), name); + return CreateItemByName(Person.GetPath, name); } /// @@ -875,7 +880,7 @@ namespace Emby.Server.Implementations.Library /// Task{Studio}. public Studio GetStudio(string name) { - return CreateItemByName(Studio.GetPath(name), name); + return CreateItemByName(Studio.GetPath, name); } /// @@ -885,7 +890,7 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public Genre GetGenre(string name) { - return CreateItemByName(Genre.GetPath(name), name); + return CreateItemByName(Genre.GetPath, name); } /// @@ -895,7 +900,7 @@ namespace Emby.Server.Implementations.Library /// Task{MusicGenre}. public MusicGenre GetMusicGenre(string name) { - return CreateItemByName(MusicGenre.GetPath(name), name); + return CreateItemByName(MusicGenre.GetPath, name); } /// @@ -905,7 +910,7 @@ namespace Emby.Server.Implementations.Library /// Task{GameGenre}. public GameGenre GetGameGenre(string name) { - return CreateItemByName(GameGenre.GetPath(name), name); + return CreateItemByName(GameGenre.GetPath, name); } /// @@ -923,7 +928,7 @@ namespace Emby.Server.Implementations.Library var name = value.ToString(CultureInfo.InvariantCulture); - return CreateItemByName(Year.GetPath(name), name); + return CreateItemByName(Year.GetPath, name); } /// @@ -933,10 +938,10 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public MusicArtist GetArtist(string name) { - return CreateItemByName(MusicArtist.GetPath(name), name); + return CreateItemByName(MusicArtist.GetPath, name); } - private T CreateItemByName(string path, string name) + private T CreateItemByName(Func getPathFn, string name) where T : BaseItem, new() { if (typeof(T) == typeof(MusicArtist)) @@ -957,7 +962,9 @@ namespace Emby.Server.Implementations.Library } } - var id = GetNewItemId(path, typeof(T)); + var path = getPathFn(name); + var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds; + var id = GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); var item = GetItemById(id) as T; diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index e010f122c..02154b98d 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Api config.EnableSeriesPresentationUniqueKey = true; config.EnableLocalizedGuids = true; config.EnableSimpleArtistDetection = true; + config.EnableNormalizedItemByNameIds = true; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 20b2529c0..8d83f8a35 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -289,7 +289,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 74679b474..e26e0dfce 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -118,7 +118,12 @@ namespace MediaBrowser.Controller.Entities.Audio return LibraryManager.GetItemList(query); } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index 22a8675c5..4187167b9 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -96,7 +96,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 1b746ae51..9769efdd0 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -108,7 +108,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index ee1aea938..b68681d36 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -133,7 +133,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validFilename = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index b8ad691a9..2e5e6ce43 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -114,7 +114,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 75fb69435..b352950a0 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -122,7 +122,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 5615649c2..af1c0d12e 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -44,6 +44,8 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + + Task> DiscoverDevices(int discoveryDurationMs); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c2b1e3c89..0562d0ac5 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableHttps { get; set; } public bool EnableSeriesPresentationUniqueKey { get; set; } public bool EnableLocalizedGuids { get; set; } + public bool EnableNormalizedItemByNameIds { get; set; } /// /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 4b70f3362..e7dbf6cb1 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -14,6 +14,8 @@ namespace MediaBrowser.Model.Net /// A implementation. ISocket CreateUdpSocket(int localPort); + ISocket CreateUdpBroadcastSocket(int localPort); + ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); /// diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 40cef4ec0..115ba25ce 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -4,6 +4,7 @@ using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.IO; namespace MediaBrowser.Model.Services { @@ -154,6 +155,6 @@ namespace MediaBrowser.Model.Services //Add Metadata to Response Dictionary Items { get; } - Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken); + Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken); } } diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index d8011f05e..d9f91c0cc 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -515,9 +515,9 @@ namespace SocketHttpListener.Net cookies.Add(cookie); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, cancellationToken); + return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } \ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index ccc0efc55..19821f954 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -307,13 +307,13 @@ namespace SocketHttpListener.Net throw new NotSupportedException(); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked) //{ // return TransmitFileOverSocket(path, offset, count, cancellationToken); //} - return TransmitFileManaged(path, offset, count, cancellationToken); + return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } private readonly byte[] _emptyBuffer = new byte[] { }; @@ -334,7 +334,7 @@ namespace SocketHttpListener.Net await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false); } - private async Task TransmitFileManaged(string path, long offset, long count, CancellationToken cancellationToken) + private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { var chunked = response.SendChunked; @@ -343,7 +343,7 @@ namespace SocketHttpListener.Net await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false); } - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true)) { if (offset > 0) { -- cgit v1.2.3 From caaa906604c759e6899ebf6be6f5ac4f9845db84 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 15:00:35 -0400 Subject: update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 248 +++++++++------------------ Emby.Server.Core/IO/LibraryMonitor.cs | 11 -- Emby.Server.Implementations/Udp/UdpServer.cs | 26 ++- MediaBrowser.Model/Net/ISocket.cs | 1 + RSSDP/SsdpCommunicationsServer.cs | 10 +- 5 files changed, 103 insertions(+), 193 deletions(-) (limited to 'MediaBrowser.Model/Net') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 45cb42ecd..94d073bd2 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -19,12 +19,20 @@ namespace Emby.Common.Implementations.Net private Socket _Socket; private int _LocalPort; - private SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() + private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() + { + SocketFlags = SocketFlags.None + }; + + private readonly SocketAsyncEventArgs _sendSocketAsyncEventArgs = new SocketAsyncEventArgs() { SocketFlags = SocketFlags.None }; private TaskCompletionSource _currentReceiveTaskCompletionSource; + private TaskCompletionSource _currentSendTaskCompletionSource; + + private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1); public UdpSocket(Socket socket, int localPort, IPAddress ip) { @@ -41,9 +49,13 @@ namespace Emby.Common.Implementations.Net private void InitReceiveSocketAsyncEventArgs() { - var buffer = new byte[8192]; - _receiveSocketAsyncEventArgs.SetBuffer(buffer, 0, buffer.Length); + var receiveBuffer = new byte[8192]; + _receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; + + var sendBuffer = new byte[8192]; + _sendSocketAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length); + _sendSocketAsyncEventArgs.Completed += _sendSocketAsyncEventArgs_Completed; } private void _receiveSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) @@ -53,13 +65,38 @@ namespace Emby.Common.Implementations.Net { _currentReceiveTaskCompletionSource = null; - tcs.TrySetResult(new SocketReceiveResult + if (e.SocketError == SocketError.Success) { - Buffer = e.Buffer, - ReceivedBytes = e.BytesTransferred, - RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), - LocalIPAddress = LocalIPAddress - }); + tcs.TrySetResult(new SocketReceiveResult + { + Buffer = e.Buffer, + ReceivedBytes = e.BytesTransferred, + RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), + LocalIPAddress = LocalIPAddress + }); + } + else + { + tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + } + } + } + + private void _sendSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + var tcs = _currentSendTaskCompletionSource; + if (tcs != null) + { + _currentSendTaskCompletionSource = null; + + if (e.SocketError == SocketError.Success) + { + tcs.TrySetResult(e.BytesTransferred); + } + else + { + tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + } } } @@ -79,8 +116,6 @@ namespace Emby.Common.Implementations.Net private set; } - #region ISocket Members - public Task ReceiveAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -90,31 +125,15 @@ namespace Emby.Common.Implementations.Net EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); cancellationToken.Register(() => tcs.TrySetCanceled()); -#if NETSTANDARD1_6 - var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); - state.TaskCompletionSource = tcs; - - _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer), SocketFlags.None, state.RemoteEndPoint) - .ContinueWith((task, asyncState) => - { - if (task.Status != TaskStatus.Faulted) - { - var receiveState = asyncState as AsyncReceiveState; - receiveState.RemoteEndPoint = task.Result.RemoteEndPoint; - ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress); - } - }, state); -#else - //var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); - //state.TaskCompletionSource = tcs; - - //_Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); - _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; _currentReceiveTaskCompletionSource = tcs; - var isPending = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); -#endif + var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); + + if (!willRaiseEvent) + { + _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + } return tcs.Task; } @@ -126,60 +145,42 @@ namespace Emby.Common.Implementations.Net if (buffer == null) throw new ArgumentNullException("messageData"); if (endPoint == null) throw new ArgumentNullException("endPoint"); - var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(); + + cancellationToken.Register(() => tcs.TrySetCanceled()); + + _sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); + _sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); + _currentSendTaskCompletionSource = tcs; -#if NETSTANDARD1_6 + var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); - if (size != buffer.Length) + if (!willRaiseEvent) { - byte[] copy = new byte[size]; - Buffer.BlockCopy(buffer, 0, copy, 0, size); - buffer = copy; + _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); } - cancellationToken.ThrowIfCancellationRequested(); + return tcs.Task; + } + + public async Task SendWithLockAsync(byte[] buffer, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken) + { + ThrowIfDisposed(); - _Socket.SendTo(buffer, ipEndPoint); - return Task.FromResult(true); -#else - var taskSource = new TaskCompletionSource(); + await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { - _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result => - { - if (cancellationToken.IsCancellationRequested) - { - taskSource.TrySetCanceled(); - return; - } - try - { - _Socket.EndSend(result); - taskSource.TrySetResult(true); - } - catch (Exception ex) - { - taskSource.TrySetException(ex); - } - - }, null); + await SendAsync(buffer, size, endPoint, cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + finally { - taskSource.TrySetException(ex); + _sendLock.Release(); } - - //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); - - return taskSource.Task; -#endif } - #endregion - - #region Overrides - protected override void Dispose(bool disposing) { if (disposing) @@ -187,44 +188,19 @@ namespace Emby.Common.Implementations.Net var socket = _Socket; if (socket != null) socket.Dispose(); - } - } - - #endregion - #region Private Methods + _sendLock.Dispose(); - private static void ProcessResponse(AsyncReceiveState state, Func receiveData, IpAddressInfo localIpAddress) - { - try - { - var bytesRead = receiveData(); - - var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.TrySetResult( - new SocketReceiveResult - { - Buffer = state.Buffer, - ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), - LocalIPAddress = localIpAddress - } - ); - } - catch (ObjectDisposedException) - { - state.TaskCompletionSource.TrySetCanceled(); - } - catch (SocketException se) - { - if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) - state.TaskCompletionSource.TrySetException(se); - else - state.TaskCompletionSource.TrySetCanceled(); - } - catch (Exception ex) - { - state.TaskCompletionSource.TrySetException(ex); + var tcs = _currentReceiveTaskCompletionSource; + if (tcs != null) + { + tcs.TrySetCanceled(); + } + var sendTcs = _currentSendTaskCompletionSource; + if (sendTcs != null) + { + sendTcs.TrySetCanceled(); + } } } @@ -237,59 +213,5 @@ namespace Emby.Common.Implementations.Net return NetworkManager.ToIpEndPointInfo(endpoint); } - - private void ProcessResponse(IAsyncResult asyncResult) - { -#if NET46 - var state = asyncResult.AsyncState as AsyncReceiveState; - try - { - var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); - - var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.TrySetResult( - new SocketReceiveResult - { - Buffer = state.Buffer, - ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), - LocalIPAddress = LocalIPAddress - } - ); - } - catch (ObjectDisposedException) - { - state.TaskCompletionSource.TrySetCanceled(); - } - catch (Exception ex) - { - state.TaskCompletionSource.TrySetException(ex); - } -#endif - } - - #endregion - - #region Private Classes - - private class AsyncReceiveState - { - public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint) - { - this.Socket = socket; - this.RemoteEndPoint = remoteEndPoint; - } - - public EndPoint RemoteEndPoint; - public byte[] Buffer = new byte[8192]; - - public Socket Socket { get; private set; } - - public TaskCompletionSource TaskCompletionSource { get; set; } - - } - - #endregion - } } diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs index 4df9b930e..e1e3186c3 100644 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ b/Emby.Server.Core/IO/LibraryMonitor.cs @@ -421,17 +421,6 @@ namespace Emby.Server.Core.IO var path = e.FullPath; - // For deletes, use the parent path - if (e.ChangeType == WatcherChangeTypes.Deleted) - { - var parentPath = Path.GetDirectoryName(path); - - if (!string.IsNullOrWhiteSpace(parentPath)) - { - path = parentPath; - } - } - ReportFileSystemChanged(path); } catch (Exception ex) diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index bb303d8fa..21ef3cab6 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -203,19 +203,6 @@ namespace Emby.Server.Implementations.Udp GC.SuppressFinalize(this); } - /// - /// Stops this instance. - /// - public void Stop() - { - _isDisposed = true; - - if (_udpClient != null) - { - _udpClient.Dispose(); - } - } - /// /// Releases unmanaged and - optionally - managed resources. /// @@ -224,7 +211,12 @@ namespace Emby.Server.Implementations.Udp { if (dispose) { - Stop(); + _isDisposed = true; + + if (_udpClient != null) + { + _udpClient.Dispose(); + } } } @@ -247,9 +239,13 @@ namespace Emby.Server.Implementations.Udp try { - await _udpClient.SendAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false); + await _udpClient.SendWithLockAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false); _logger.Info("Udp message sent to {0}", remoteEndPoint); + } + catch (OperationCanceledException) + { + } catch (Exception ex) { diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs index 90070b128..61fc0e28b 100644 --- a/MediaBrowser.Model/Net/ISocket.cs +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -24,5 +24,6 @@ namespace MediaBrowser.Model.Net /// Sends a UDP message to a particular end point (uni or multicast). /// Task SendAsync(byte[] buffer, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); + Task SendWithLockAsync(byte[] buffer, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 99e3969aa..cc464e689 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -177,11 +177,15 @@ namespace Rssdp.Infrastructure { try { - await socket.SendAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); + await socket.SendWithLockAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); } catch (ObjectDisposedException) { + } + catch (OperationCanceledException) + { + } catch (Exception ex) { @@ -341,11 +345,9 @@ namespace Rssdp.Infrastructure foreach (var socket in sockets) { - await socket.SendAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); + await SendFromSocket(socket, messageData, destination, cancellationToken).ConfigureAwait(false); } } - - ThrowIfDisposed(); } private ISocket ListenForBroadcastsAsync() -- cgit v1.2.3