当客户端失去连接时,SignalR Owin Linux / Mono Socket上的自主机

时间:2014-04-29 15:25:01

标签: c# linux mono signalr socketexception

我在ubuntu服务器14.04上运行非常简单的信号服务器,通过Owin自托管,单声道3.2.8。 (以下代码)。

连接/断开连接在远程Windows服务器和我将位部署到Linux服务器时都能正常工作。但是当客户端意外死亡而不是告诉信号器他正在断开连接时,那只是在我只在Linux服务器上得到一个永无止境的SocketException时。 Windows服务器在大约30秒左右后断开客户端,但是linux服务器每隔10秒左右就会发出socketexception(也在下面)。

如何在运行相同代码时使linux服务器像windows服务器一样,在设置超时后断开用户并且不抛出socketexceptions?

服务器代码:

using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Owin;

namespace signalrtestserver
{
    class Program
    {
        static void Main(string[] args)
        {
            var uri = System.Configuration.ConfigurationManager.AppSettings["startup_uri"] ?? "http://*:7890";
            using (Microsoft.Owin.Hosting.WebApp.Start<Startup>(uri))
            {
                Console.WriteLine(string.Format("Server started on {0}. Press enter to close.", uri));
                Console.ReadLine();
            }
        }
    }

    class Startup
    {
        static Hub hub;

        public void Configuration(IAppBuilder app)
        {
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            var configuration = new HubConfiguration();
            configuration.EnableDetailedErrors = true;
            app.MapSignalR("/signalr", configuration);
            hub = new MyHub();
        }
    }

    public class MyHub : Hub
    {
        public override Task OnConnected() { Console.WriteLine(Context.ConnectionId + " connected"); return base.OnConnected(); }
        public override Task OnDisconnected() { Console.WriteLine(Context.ConnectionId + " disconnected"); return base.OnDisconnected(); }
        public override Task OnReconnected() { Console.WriteLine(Context.ConnectionId + " reconnected"); return base.OnReconnected(); }
    }
}

客户代码:

using System;
using System.Net;
using Microsoft.AspNet.SignalR.Client;

namespace signalrconnection
{
    class Program
    {
        static void Main(string[] args)
        {
            var uri = System.Configuration.ConfigurationManager.AppSettings["signalr_uri"] ?? "http://localhost:7890/signalr";
            ServicePointManager.DefaultConnectionLimit = 10;
            var hubConnection = new HubConnection(uri, false);
            hubConnection.StateChanged += stateChange => Console.WriteLine(string.Format("SignalR {0} >> {1} ({2})", stateChange.OldState, stateChange.NewState, hubConnection.Transport == null ? "<<null>>" : hubConnection.Transport.Name));
            var hubProxy = hubConnection.CreateHubProxy("MyHub");
            hubConnection.Start();
            Console.WriteLine("Press enter to die...");
            Console.ReadLine();
            //hubConnection.Dispose(); //uncomment this to simulate a graceful disconnect which works on both windows and linux
        }
    }
}

永不结束单声道异常:

{path-to-project}/Microsoft.AspNet.SignalR.Core.dll Error : 0 : SignalR exception thrown by Task: System.AggregateException: One or more errors occured ---> System.IO.IOException: Write failure ---> System.Net.Sockets.SocketException: Connection reset by peer
  at System.Net.Sockets.Socket.Send (System.Byte[] buf, Int32 offset, Int32 size, SocketFlags flags) [0x00000] in <filename unknown>:0
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.InternalWrite (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.RequestProcessing.ExceptionFilterStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
 --> (Inner exception 0) System.IO.IOException: Write failure ---> System.Net.Sockets.SocketException: Connection reset by peer
  at System.Net.Sockets.Socket.Send (System.Byte[] buf, Int32 offset, Int32 size, SocketFlags flags) [0x00000] in <filename unknown>:0
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.InternalWrite (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.RequestProcessing.ExceptionFilterStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0

非常感谢任何帮助。提前谢谢!

4 个答案:

答案 0 :(得分:5)

问题是此行static Hub hub,以及启动方法中的这一行hub = new MyHub()

您不需要显式创建Hub类的实例,也不需要保留引用。每次向服务器发出请求时都会实例化集线器类。看到 http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#transience关于集线器对象生存期的部分。

答案 1 :(得分:3)

这意味着每当断开连接时,Linux中的Mono就会抛出与Windows中的MS.NET不同的异常。 SignalR由最有可能使用MS.NET的人实施,因此SignalR必须期待某些异常,并且对此有所了解。

解决此问题的最佳方法是调试代码步进SignalR实现(在使用Mono的Linux上运行时),以查看捕获的异常位置,并与Windows中MS.NET下的SignalR进行比较。然后创建一个关于差异的最小测试用例并在http://bugzilla.xamarin.com/中提交一个错误,他们可能会很快修复它(或者你可以将它分配给我,我会看看它,因为我有兴趣运行SignalR在单声道下。)

答案 2 :(得分:3)

我在Raspberry Pi上使用简单的Owin自托管演示应用程序遇到了同样的问题。通过按住F5从单个浏览器请求“欢迎”页面足以在15秒内终止主机。我还没有进行任何自动压力测试,因为我的“用户验收”测试立刻失败了。

我找到的可靠解决方案是创建自定义OwinMiddleware以捕获并阻塞套接字异常:

class CustomExceptionMiddleware : OwinMiddleware
{
    public CustomExceptionMiddleware(OwinMiddleware next)
        : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        try
        {
            await Next.Invoke(context);
        }
        catch (IOException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

需要更新Owin启动以使用中间件:

using Owin;

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use<OwnExceptionMiddleware>()
           .UseNancy();
    }
}

受到http://dhickey.ie/2014/02/bubbling-exceptions-in-nancy-up-the-owin-pipeline/

的启发

以下是我的例外情况:

Unhandled Exception:
System.IO.IOException: Write failure ---> System.Net.Sockets.SocketException: The socket has been shut down
  at System.Net.Sockets.Socket.Send (System.Byte[] buf, Int32 offset, Int32 size, SocketFlags flags) [0x00000] in <filename unknown>:0
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.Sockets.NetworkStream.Write (System.Byte[] buffer, Int32 offset, Int32 size) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.InternalWrite (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at System.Net.ResponseStream.Close () [0x00000] in <filename unknown>:0
  at System.Net.HttpConnection.Close (Boolean force_close) [0x00000] in <filename unknown>:0
  at System.Net.HttpListenerResponse.Close (Boolean force) [0x00000] in <filename unknown>:0
  at System.Net.HttpListenerResponse.Abort () [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.RequestProcessing.OwinHttpListenerResponse.End () [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.RequestProcessing.OwinHttpListenerContext.End () [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.RequestProcessing.OwinHttpListenerContext.End (System.Exception ex) [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.OwinHttpListener+<ProcessRequestAsync>d__5.MoveNext () [0x00000] in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0
  at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <filename unknown>:0
  at Microsoft.Owin.Host.HttpListener.OwinHttpListener+<ProcessRequestsAsync>d__0.MoveNext () [0x00000] in <filename unknown>:0

考虑具体截取您拦截的例外情况:

    public override async Task Invoke(IOwinContext context)
    {
        try
        {
            await Next.Invoke(context);
        }
        catch (IOException ex)
        {
            if (ex.HResult == -2146233087) // The socket has been shut down
            {
                NLog.LogManager.GetLogger("OwinExceptionHandler").Trace(ex);
            }
            else
            {
                throw;
            }

        }
    }

答案 3 :(得分:1)

我想我有同样的问题。我描述了它:

我找到了适合我情况的解决方案。我将HttpListener.IgnoreWriteExceptions属性设置为&#39; true&#39;在OWIN配置方法中然后注册中间件。例如:

internal class Startup
{
    public void Configuration(IAppBuilder app)
    {
        object httpListener;

        if (app.Properties.TryGetValue(typeof(HttpListener).FullName, out httpListener)
            && httpListener is HttpListener)
        {
            // HttpListener should not return exceptions that occur
            // when sending the response to the client
            ((HttpListener)httpListener).IgnoreWriteExceptions = true;
        }

        app.Use<TestOwinMiddleware>();
    }
}

我希望这对你有所帮助。