SignalR .NET客户端不支持Windows 7上的WebSockets
我已经使用SignalR编写了一个小型回声服务器(.net 4.5),控制台客户端(.net 4.5)和Web客户端,并给出了示例here。SignalR .NET客户端不支持Windows 7上的WebSockets
服务器托管在IIS8/Win8中。然后我在Win7上运行了两个客户端。我发现Chrome中的Web客户端使用webSockets,而控制台应用程序客户端使用serverSentEvents。如果我在Win8上运行控制台客户端,那么webSockets传输正在使用中。
确实SignalR .NET客户端只能在Win8及更高版本上使用webSockets?
正确:.NET客户端仅在Win8及更高版本上使用WebSocket。
对于一个项目,我不得不使用真正的websocket连接与SignalR结合使用。
对于不支持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);
您是如何在.NET 4.0上为服务器端添加websocket支持的? – sqenixs 2016-08-10 20:11:00
谢谢古斯塔沃。我在Windows 7上调试过SignalR客户端,发现ClientWebSocket ctor抛出NotSupportedException异常。根据[MSDN](http://msdn.microsoft.com/zh-cn/library/system.net.websockets.clientwebsocket.aspx)支持的平台是Windows 8,Windows Server 2012. – 2013-03-15 09:49:53
Peter给出的答案可能成为解决此限制的解决方案。 – 2016-01-27 10:56:23