Note: From version 3.6.0 this library support .NET Core.
For PCL Profile111 compatibility use legacy version 1.6.2:
Please star this project if you find it useful. Thank you.
This library is a ground-up implementation of the Websocket specification (RFC 6544). The implementation does not rely on the build-in Websocket libraries in .NET and UWP etc.
The library allows developers to establish secure wss websocket connections to websocket servers that have self-signing certificates, expired certificates etc. This capability should be used with care, but is nice to have in testing environments or close local networks IoT set-ups etc. To use this set the ConnectAsync parameter ignoreServerCertificateErrors: true
.
This project is based on SocketLite.PCL for cross platform TCP sockets support.
This project utilizes Reactive Extensions. Although this has an added learning curve its a learning worth while and it makes creating a library like this much more elegant compared to using call-back or events.
The library is easy to use, as illustated with the examples below.
For a more elaborate example see this code.
using ISocketLite.PCL.Model;
using IWebsocketClientLite.PCL;
using WebsocketClientLite.PCL;
class Program
{
private static IDisposable _subscribeToMessagesReceived;
static void Main(string[] args)
{
StartWebSocket();
System.Console.ReadKey();
// Don't forget to clean-up with Dispose. It doesn't matter here, but it will in your code.
_subscribeToMessagesReceived.Dispose();
}
static async void StartWebSocket()
{
var websocketClient = new MessageWebSocketRx();
// 1. Start by subscribing to messages.
_subscribeToMessagesReceived = websocketClient.ObserveTextMessagesReceived.Subscribe(
msg =>
{
//Insert your code managing incoming messages here
System.Console.WriteLine($"Reply from test server: {msg}");
},
ex =>
{
//Insert your code managing incoming exception here
System.Console.WriteLine(ex.Message);
},
() =>
{
//Insert your code handling the completion of the subscription here
System.Console.WriteLine($"Subscription Completed");
});
// 2a. ### Optional Subprotocols ###
// The echo.websocket.org does not support any sub-protocols and hence this test does not add any.
// Adding a sub-protocol that the server does not support causes the client to close down the connection.
// Anyhow here is how to add
// List<string> subprotocols = new List<string> {"soap", "json"};
List<string> subprotocols = null;
// 2b. ### Optional headers
// Adding headers are easy
var headers = new Dictionary<string, string>{{"Pragma", "no-cache"}, {"Cache-Control", "no-cache"}};
// 3. Now establish a connection to the server
await websocketClient.ConnectAsync(new Uri("wss://echo.websocket.org:443"), // use the publicly available test server: http://www.websocket.org/echo.html
ignoreServerCertificateErrors: false, // you can ignore server certificate errors. Good for test, but be careful!
headers: headers,
subprotocols:subprotocols,
tlsProtocolVersion:TlsProtocolVersion.Tls12);
// 4. send a test to the echo server. It will reply back with what you send.
await websocketClient.SendTextAsync("Test Single Frame");
// 5. you can also send multiple frames in one batch.
var strArray = new[] {"Test ", "multiple ", "frames"};
await websocketClient.SendTextAsync(strArray);
// 6. or you can send frames one by one. Start with FrameType.FirstOfMultipleFrames
await websocketClient.SendTextMultiFrameAsync("Start ", FrameType.FirstOfMultipleFrames);
await Task.Delay(TimeSpan.FromMilliseconds(200));
// ... continue with: FrameType.Continuation
await websocketClient.SendTextMultiFrameAsync("Continue... #1 ", FrameType.Continuation);
await Task.Delay(TimeSpan.FromMilliseconds(300));
await websocketClient.SendTextMultiFrameAsync("Continue... #2 ", FrameType.Continuation);
await Task.Delay(TimeSpan.FromMilliseconds(150));
await websocketClient.SendTextMultiFrameAsync("Continue... #3 ", FrameType.Continuation);
await Task.Delay(TimeSpan.FromMilliseconds(400));
// Don't forget the last stop frame!
await websocketClient.SendTextMultiFrameAsync("This is the last Stop Frame.", FrameType.LastInMultipleFrames);
}
}
The RFC 6455 section defining how ping/pong works seem to be ambigious on the question of whether or not a pong should include the byte defining the length of "Application Data" when the length is zero.
When testing against websocket.org the byte is expected with the value of zero, however when used with the slack.rtm api the byte should not be there or the slack websocket server will disconnect.
To manage this byte the following connect parameter can be set to true. Like this:
await _webSocket.ConnectAsync(_uri, excludeZeroApplicationDataInPong:true);
To futher complicate matters the slack.rtm api also seems to requires a ping at the Slack application layer too. A simplified implementation of this could look like this:
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(30));
await _webSocket.SendTextAsync("{\"id\": 1234, // ID, see \"sending messages\" above\"type\": \"ping\",...}");
}
For details read the Ping and Pong section of the slack.rtm api documentation
Monitoring connection status is easy:
var websocketLoggerSubscriber = websocketClient.ObserveConnectionStatus.Subscribe(
status =>
{
// Insert code here for logging or handling connection status
System.Console.WriteLine(status.ToString());
});
####References: The following documentation was utilized when writting this library: