7

ここに示す例とSignalRを使用して、小さなエコーサーバー(.net 4.5)、コンソールクライアント(.net 4.5)、およびWebクライアントを作成しました。

サーバーはIIS8/Win8でホストされています。次に、Win7で両方のクライアントを実行しました。ChromeのWebクライアントはwebSocketsを使用し、コンソールアプリクライアントはserverSentEventsを使用していることがわかります。Win8でコンソールクライアントを実行すると、webSocketsトランスポートが使用されます。

SignalR .NETクライアントがWin8以降でのみwebSocketを使用するというのは本当ですか?

4

3 に答える 3

11

正解です。.NETクライアントはWin8以降でのみWebSocketを使用します。

于 2013-03-14T17:30:19.690 に答える
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);
于 2016-01-03T21:38:41.837 に答える