-
-
Save ggkollective/3c9a79ab9ca480a4561a700a33e457c4 to your computer and use it in GitHub Desktop.
Wrapper for the .NET 4.5 ClientWebSocket
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Net.WebSockets; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace Coe.WebSocketWrapper | |
{ | |
public class WebSocketWrapper | |
{ | |
private const int ReceiveChunkSize = 1024; | |
private const int SendChunkSize = 1024; | |
private readonly ClientWebSocket _ws; | |
private readonly Uri _uri; | |
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); | |
private readonly CancellationToken _cancellationToken; | |
private Action<WebSocketWrapper> _onConnected; | |
private Action<string, WebSocketWrapper> _onMessage; | |
private Action<WebSocketWrapper> _onDisconnected; | |
protected WebSocketWrapper(string uri) | |
{ | |
_ws = new ClientWebSocket(); | |
_ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(20); | |
_uri = new Uri(uri); | |
_cancellationToken = _cancellationTokenSource.Token; | |
} | |
/// <summary> | |
/// Creates a new instance. | |
/// </summary> | |
/// <param name="uri">The URI of the WebSocket server.</param> | |
/// <returns></returns> | |
public static WebSocketWrapper Create(string uri) | |
{ | |
return new WebSocketWrapper(uri); | |
} | |
/// <summary> | |
/// Connects to the WebSocket server. | |
/// </summary> | |
/// <returns></returns> | |
public WebSocketWrapper Connect() | |
{ | |
ConnectAsync(); | |
return this; | |
} | |
/// <summary> | |
/// Set the Action to call when the connection has been established. | |
/// </summary> | |
/// <param name="onConnect">The Action to call.</param> | |
/// <returns></returns> | |
public WebSocketWrapper OnConnect(Action<WebSocketWrapper> onConnect) | |
{ | |
_onConnected = onConnect; | |
return this; | |
} | |
/// <summary> | |
/// Set the Action to call when the connection has been terminated. | |
/// </summary> | |
/// <param name="onDisconnect">The Action to call</param> | |
/// <returns></returns> | |
public WebSocketWrapper OnDisconnect(Action<WebSocketWrapper> onDisconnect) | |
{ | |
_onDisconnected = onDisconnect; | |
return this; | |
} | |
/// <summary> | |
/// Set the Action to call when a messages has been received. | |
/// </summary> | |
/// <param name="onMessage">The Action to call.</param> | |
/// <returns></returns> | |
public WebSocketWrapper OnMessage(Action<string, WebSocketWrapper> onMessage) | |
{ | |
_onMessage = onMessage; | |
return this; | |
} | |
/// <summary> | |
/// Send a message to the WebSocket server. | |
/// </summary> | |
/// <param name="message">The message to send</param> | |
public void SendMessage(string message) | |
{ | |
SendMessageAsync(message); | |
} | |
private async void SendMessageAsync(string message) | |
{ | |
if (_ws.State != WebSocketState.Open) | |
{ | |
throw new Exception("Connection is not open."); | |
} | |
var messageBuffer = Encoding.UTF8.GetBytes(message); | |
var messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / SendChunkSize); | |
for (var i = 0; i < messagesCount; i++) | |
{ | |
var offset = (SendChunkSize * i); | |
var count = SendChunkSize; | |
var lastMessage = ((i + 1) == messagesCount); | |
if ((count * (i + 1)) > messageBuffer.Length) | |
{ | |
count = messageBuffer.Length - offset; | |
} | |
await _ws.SendAsync(new ArraySegment<byte>(messageBuffer, offset, count), WebSocketMessageType.Text, lastMessage, _cancellationToken); | |
} | |
} | |
private async void ConnectAsync() | |
{ | |
await _ws.ConnectAsync(_uri, _cancellationToken); | |
CallOnConnected(); | |
StartListen(); | |
} | |
private async void StartListen() | |
{ | |
var buffer = new byte[ReceiveChunkSize]; | |
try | |
{ | |
while (_ws.State == WebSocketState.Open) | |
{ | |
var stringResult = new StringBuilder(); | |
WebSocketReceiveResult result; | |
do | |
{ | |
result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cancellationToken); | |
if (result.MessageType == WebSocketMessageType.Close) | |
{ | |
await | |
_ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |
CallOnDisconnected(); | |
} | |
else | |
{ | |
var str = Encoding.UTF8.GetString(buffer, 0, result.Count); | |
stringResult.Append(str); | |
} | |
} while (!result.EndOfMessage); | |
CallOnMessage(stringResult); | |
} | |
} | |
catch (Exception) | |
{ | |
CallOnDisconnected(); | |
} | |
finally | |
{ | |
_ws.Dispose(); | |
} | |
} | |
private void CallOnMessage(StringBuilder stringResult) | |
{ | |
if (_onMessage != null) | |
RunInTask(() => _onMessage(stringResult.ToString(), this)); | |
} | |
private void CallOnDisconnected() | |
{ | |
if (_onDisconnected != null) | |
RunInTask(() => _onDisconnected(this)); | |
} | |
private void CallOnConnected() | |
{ | |
if (_onConnected != null) | |
RunInTask(() => _onConnected(this)); | |
} | |
private static void RunInTask(Action action) | |
{ | |
Task.Factory.StartNew(action); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment