SignalR .NET客户端不支持Windows 7上的WebSockets

时间:2013-03-13 13:20:59

标签: windows-7 signalr .net-4.5 signalr.client

我使用SignalR编写了一个小型echo服务器(.net 4.5),控制台客户端(.net 4.5)和Web客户端,并提供了示例here

服务器托管在IIS8 / Win8中。然后我在Win7上运行了两个客户端。我发现Chrome中的Web客户端使用webSockets,而控制台应用客户端使用serverSentEvents。如果我在Win8上运行控制台客户端,则webSockets传输正在使用中。

SignalR .NET客户端是否仅在Win8及更高版本上使用webSockets?

3 个答案:

答案 0 :(得分:11)

这是正确的:.NET客户端仅在Win8及更高版本上使用WebSockets。

答案 1 :(得分:5)

对于一个项目,我必须结合SignalR使用真正的websocket连接。

对于不支持websockets的Windows版本,您可以使用WebSocket4Net NuGet包和SignalR IClientTransport的以下实现。

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Http;
using Microsoft.AspNet.SignalR.Client.Infrastructure;
using Microsoft.AspNet.SignalR.Client.Transports;
using SuperSocket.ClientEngine;
using WebSocket4Net;

public sealed class WebSocket4NetTransport : ClientTransportBase
{
  private IConnection _connection;
  private string _connectionData;
  private CancellationToken _disconnectToken;
  private CancellationTokenSource _webSocketTokenSource;
  private WebSocket _webSocket4Net;
  private int _disposed;

  public TimeSpan ReconnectDelay { get; set; }

  public WebSocket4NetTransport()
    : this(new DefaultHttpClient())
  {
  }

  public WebSocket4NetTransport(IHttpClient client)
    : base(client, "webSockets")
  {
    _disconnectToken = CancellationToken.None;
    ReconnectDelay = TimeSpan.FromSeconds(2.0);
  }

  ~WebSocket4NetTransport()
  {
    Dispose(false);
  }

  protected override void OnStart(IConnection connection, string connectionData, CancellationToken disconnectToken)
  {
    _connection = connection;
    _connectionData = connectionData;
    _disconnectToken = disconnectToken;

    var connectUrl = UrlBuilder.BuildConnect(connection, Name, connectionData);

    try
    {
      PerformConnect(connectUrl);
    }
    catch(Exception ex)
    {
      TransportFailed(ex);
    }
  }

  protected override void OnStartFailed()
  {
    Dispose();
  }

  public override Task Send(IConnection connection, string data, string connectionData)
  {
    if(_webSocket4Net.State == WebSocketState.Open)
    {
      _webSocket4Net.Send(data);
    }

    var ex = new InvalidOperationException("Socket closed");
    connection.OnError(ex);

    throw ex;
  }

  public override void LostConnection(IConnection connection)
  {
    _connection.Trace(TraceLevels.Events, "WS: LostConnection");

    if(_webSocketTokenSource == null)
    {
      return;
    }

    _webSocketTokenSource.Cancel();
  }

  public override bool SupportsKeepAlive
  {
    get { return true; }
  }

  protected override void Dispose(bool disposing)
  {
    if(disposing)
    {
      if(Interlocked.Exchange(ref _disposed, 1) == 1)
      {
        base.Dispose(true);
        return;
      }

      if(_webSocketTokenSource != null)
      {
        _webSocketTokenSource.Cancel();
      }

      if(_webSocket4Net != null)
      {
        DisposeWebSocket4Net();
      }

      if(_webSocketTokenSource != null)
      {
        _webSocketTokenSource.Dispose();
      }
    }

    base.Dispose(disposing);
  }

  private void DisposeWebSocket4Net()
  {
    _webSocket4Net.Error -= WebSocketOnError;
    _webSocket4Net.Opened -= WebSocketOnOpened;
    _webSocket4Net.Closed -= WebSocketOnClosed;
    _webSocket4Net.MessageReceived -= WebSocketOnMessageReceived;

    _webSocket4Net.Dispose();
    _webSocket4Net = null;
  }

  private void PerformConnect(string url)
  {
    if(_webSocket4Net != null)
    {
      DisposeWebSocket4Net();
    }

    _webSocketTokenSource = new CancellationTokenSource();
    _webSocketTokenSource.Token.Register(WebSocketTokenSourceCanceled);
    CancellationTokenSource.CreateLinkedTokenSource(_webSocketTokenSource.Token, _disconnectToken);

    // Add the header from the connection to the socket connection
    var headers = _connection.Headers.ToList();

    // SignalR uses https, websocket4net uses wss
    url = url.Replace("http://", "ws://").Replace("https://", "wss://");

    _webSocket4Net = new WebSocket(url, customHeaderItems: headers);

    _webSocket4Net.Error += WebSocketOnError;
    _webSocket4Net.Opened += WebSocketOnOpened;
    _webSocket4Net.Closed += WebSocketOnClosed;
    _webSocket4Net.MessageReceived += WebSocketOnMessageReceived;

    _webSocket4Net.Open();
  }

  private async Task DoReconnect()
  {
    string reconnectUrl = UrlBuilder.BuildReconnect(_connection, Name, _connectionData);

    while(TransportHelper.VerifyLastActive(_connection))
    {
      if(_connection.EnsureReconnecting())
      {
        try
        {
          PerformConnect(reconnectUrl);
          break;
        }
        catch(OperationCanceledException)
        {
          break;
        }
        catch(Exception ex)
        {
          _connection.OnError(ex);
        }
        await Task.Delay(ReconnectDelay, CancellationToken.None);
      }
      else
      {
        break;
      }
    }
  }

  private void WebSocketOnOpened(object sender, EventArgs e)
  {
    _connection.Trace(TraceLevels.Events, "WS: OnOpen()");

    if(!_connection.ChangeState(ConnectionState.Reconnecting, ConnectionState.Connected))
    {
      return;
    }

    _connection.OnReconnected();
  }

  private async void WebSocketOnClosed(object sender, EventArgs e)
  {
    _connection.Trace(TraceLevels.Events, "WS: OnClose()");

    if(_disconnectToken.IsCancellationRequested || AbortHandler.TryCompleteAbort())
    {
      return;
    }

    await DoReconnect();
  }

  private void WebSocketOnError(object sender, ErrorEventArgs e)
  {
    var exception = e.Exception;
    _connection.OnError(exception);
  }

  private void WebSocketOnMessageReceived(object sender, MessageReceivedEventArgs e)
  {
    var message = e.Message;

    _connection.Trace(TraceLevels.Messages, "WS: OnMessage({0})", (object)message);
    ProcessResponse(_connection, message);
  }

  private void WebSocketTokenSourceCanceled()
  {
    if(_webSocketTokenSource.IsCancellationRequested)
    {
      if(_webSocket4Net.State != WebSocketState.Closed)
      {
        _webSocket4Net.Close(1000, "");
      }
    }
  }
}

要创建websocket客户端,请使用try-catch来确定应该使用哪个websocket实现。

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

public static class WebSocketTransportFactory
{
  public static IClientTransport Create()
  {
    IClientTransport clientTransport;

    try
    {
      // Test if .net websockets are supported
      // Supported since Windows 8 and newer
      var testSocket = new ClientWebSocket();

      clientTransport = new WebSocketTransport();
    }
    catch(PlatformNotSupportedException)
    {
      clientTransport = new WebSocket4NetTransport();
    }

    return clientTransport;
  }
}

开始连接SignalR中心。

var hubConnection = new HubConnection("https://url/to/the/hub");
var clientTransport = WebSocketTransportFactory.Create();
await hubConnection.Start(clientTransport);

答案 2 :(得分:0)

这是一个古老的问题,但是如果您将项目转换为独立的.NET核心应用程序,则可以在Windows 7上将SignalR与WebSockets一起使用。