瀏覽代碼

update msearch

Luke Pulverenti 8 年之前
父節點
當前提交
180ab02edc

+ 1 - 2
RSSDP/ISsdpCommunicationsServer.cs

@@ -51,8 +51,7 @@ namespace Rssdp.Infrastructure
         /// <summary>
         /// Sends a message to the SSDP multicast address and port.
         /// </summary>
-        /// <param name="messageData">A byte array containing the data to send.</param>
-        Task SendMulticastMessage(byte[] messageData);
+        Task SendMulticastMessage(string message);
 
         #endregion
 

+ 1 - 0
RSSDP/RSSDP.csproj

@@ -65,6 +65,7 @@
     <Compile Include="SsdpDevicePublisher.cs" />
     <Compile Include="SsdpDevicePublisherBase.cs" />
     <Compile Include="SsdpEmbeddedDevice.cs" />
+    <Compile Include="SsdpHelper.cs" />
     <Compile Include="SsdpRootDevice.cs" />
     <Compile Include="UPnP10DeviceValidator.cs" />
   </ItemGroup>

+ 1 - 1
RSSDP/SsdpDeviceLocator.cs

@@ -21,7 +21,7 @@ namespace Rssdp
 		/// Default constructor. Constructs a new instance using the default <see cref="ISsdpCommunicationsServer"/> and <see cref="ISocketFactory"/> implementations for this platform.
 		/// </summary>
 		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="Can't expose along exception paths here (exceptions should be very rare anyway, and probably fatal too) and we shouldn't dipose the items we pass to base in any other case.")]
-		public SsdpDeviceLocator(ISocketFactory socketFactory, ITimerFactory timerFacatory) : base(new SsdpCommunicationsServer(socketFactory), timerFacatory)
+		public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFacatory) : base(communicationsServer, timerFacatory)
 		{
 			// This is not the problem you are looking for;
 			// Yes, this is poor man's dependency injection which some call an anti-pattern.

+ 26 - 44
RSSDP/SsdpDeviceLocatorBase.cs

@@ -8,6 +8,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Threading;
+using RSSDP;
 
 namespace Rssdp.Infrastructure
 {
@@ -28,14 +29,6 @@ namespace Rssdp.Infrastructure
         private ITimer _ExpireCachedDevicesTimer;
         private ITimerFactory _timerFactory;
 
-        private const string HttpURequestMessageFormat = @"{0} * HTTP/1.1
-HOST: {1}:{2}
-MAN: ""{3}""
-MX: {5}
-ST: {4}
-
-";
-
         private static readonly TimeSpan DefaultSearchWaitTime = TimeSpan.FromSeconds(4);
         private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
 
@@ -166,21 +159,16 @@ ST: {4}
             if (searchWaitTime > TimeSpan.Zero)
                 await BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime)).ConfigureAwait(false);
 
-            await Task.Run(() =>
+            lock (_SearchResultsSynchroniser)
             {
-                lock (_SearchResultsSynchroniser)
+                foreach (var device in GetUnexpiredDevices().Where(NotificationTypeMatchesFilter))
                 {
-                    foreach (var device in GetUnexpiredDevices().Where((d) => NotificationTypeMatchesFilter(d)))
-                    {
-                        if (this.IsDisposed) return;
-
-                        DeviceFound(device, false);
-                    }
+                    DeviceFound(device, false);
                 }
-            }).ConfigureAwait(false);
+            }
 
             if (searchWaitTime != TimeSpan.Zero)
-                await Task.Delay(searchWaitTime);
+                await Task.Delay(searchWaitTime).ConfigureAwait(false);
 
             IEnumerable<DiscoveredSsdpDevice> retVal = null;
 
@@ -192,7 +180,7 @@ ST: {4}
                     _SearchResults = null;
                 }
 
-                var expireTask = RemoveExpiredDevicesFromCacheAsync();
+                RemoveExpiredDevicesFromCache();
             }
             finally
             {
@@ -417,25 +405,27 @@ ST: {4}
 
         #region Network Message Processing
 
-        private static byte[] BuildDiscoverMessage(string serviceType, TimeSpan mxValue)
-        {
-            return System.Text.UTF8Encoding.UTF8.GetBytes(
-                String.Format(HttpURequestMessageFormat,
-                    SsdpConstants.MSearchMethod,
-                    SsdpConstants.MulticastLocalAdminAddress,
-                    SsdpConstants.MulticastPort,
-                    SsdpConstants.SsdpDiscoverMessage,
-                    serviceType,
-                    mxValue.TotalSeconds
-                )
-            );
-        }
-
         private Task BroadcastDiscoverMessage(string serviceType, TimeSpan mxValue)
         {
-            var broadcastMessage = BuildDiscoverMessage(serviceType, mxValue);
+            var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
-            return _CommunicationsServer.SendMulticastMessage(broadcastMessage);
+            values["HOST"] = "239.255.255.250:1900";
+            values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2";
+            //values["X-EMBY-SERVERID"] = _appHost.SystemId;
+
+            values["MAN"] = "\"ssdp:discover\"";
+
+            // Search target
+            values["ST"] = "ssdp:all";
+
+            // Seconds to delay response
+            values["MX"] = "3";
+
+            var header = "M-SEARCH * HTTP/1.1";
+
+            var message = SsdpHelper.BuildMessage(header, values);
+
+            return _CommunicationsServer.SendMulticastMessage(message);
         }
 
         private void ProcessSearchResponseMessage(HttpResponseMessage message)
@@ -608,19 +598,11 @@ ST: {4}
 
         #region Expiry and Device Removal
 
-        private Task RemoveExpiredDevicesFromCacheAsync()
-        {
-            return Task.Run(() =>
-            {
-                RemoveExpiredDevicesFromCache();
-            });
-        }
-
         private void RemoveExpiredDevicesFromCache()
         {
             if (this.IsDisposed) return;
 
-            IEnumerable<DiscoveredSsdpDevice> expiredDevices = null;
+            DiscoveredSsdpDevice[] expiredDevices = null;
             lock (_Devices)
             {
                 expiredDevices = (from device in _Devices where device.IsExpired() select device).ToArray();

+ 2 - 2
RSSDP/SsdpDevicePublisher.cs

@@ -26,8 +26,8 @@ namespace Rssdp
 		/// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification.</para>
 		/// </remarks>
 		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")]
-		public SsdpDevicePublisher(ISocketFactory socketFactory, ITimerFactory timerFactory, string osName, string osVersion)
-			: base(new SsdpCommunicationsServer(socketFactory), timerFactory, osName, osVersion)
+		public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory, string osName, string osVersion)
+			: base(communicationsServer, timerFactory, osName, osVersion)
 		{
 
 		}

+ 88 - 0
RSSDP/SsdpHelper.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Text;
+
+namespace RSSDP
+{
+    public class SsdpHelper
+    {
+        private readonly ITextEncoding _encoding;
+
+        public SsdpHelper(ITextEncoding encoding)
+        {
+            _encoding = encoding;
+        }
+
+        public SsdpMessageInfo ParseSsdpResponse(byte[] data)
+        {
+            using (var ms = new MemoryStream(data))
+            {
+                using (var reader = new StreamReader(ms, _encoding.GetASCIIEncoding()))
+                {
+                    var proto = (reader.ReadLine() ?? string.Empty).Trim();
+                    var method = proto.Split(new[] { ' ' }, 2)[0];
+                    var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+                    for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
+                    {
+                        line = line.Trim();
+                        if (string.IsNullOrEmpty(line))
+                        {
+                            break;
+                        }
+                        var parts = line.Split(new[] { ':' }, 2);
+
+                        if (parts.Length >= 2)
+                        {
+                            headers[parts[0]] = parts[1].Trim();
+                        }
+                    }
+
+                    return new SsdpMessageInfo
+                    {
+                        Method = method,
+                        Headers = headers,
+                        Message = data
+                    };
+                }
+            }
+        }
+
+        public static string BuildMessage(string header, Dictionary<string, string> values)
+        {
+            var builder = new StringBuilder();
+
+            const string argFormat = "{0}: {1}\r\n";
+
+            builder.AppendFormat("{0}\r\n", header);
+
+            foreach (var pair in values)
+            {
+                builder.AppendFormat(argFormat, pair.Key, pair.Value);
+            }
+
+            builder.Append("\r\n");
+
+            return builder.ToString();
+        }
+    }
+
+    public class SsdpMessageInfo
+    {
+        public string Method { get; set; }
+
+        public IpEndPointInfo EndPoint { get; set; }
+
+        public Dictionary<string, string> Headers { get; set; }
+
+        public IpEndPointInfo LocalEndPoint { get; set; }
+        public byte[] Message { get; set; }
+
+        public SsdpMessageInfo()
+        {
+            Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        }
+    }
+}