The MessageWebSocket and StreamWebSocket classes handle errors by throwing an exception. When a network exception occurs in your app, this indicates a problem or failure. Exceptions can occur for many reasons when using the WebSocket APIs. Often the exception can result from changes in network connectivity or other networking issues with the remote server. Networking exceptions are not necessarily indicative of bugs in your app. Some exceptions are expected as part of normal operations over the network, and must be treated as such by your code.
Some common reasons for exceptions when using the WebSocket APIs include the following:
Exceptions from network errors (loss or change of connectivity, connection failures, and server failures, for example) can happen at any time. These errors result in exceptions being thrown. If not handled by your app, an exception can cause your entire app to be terminated by the runtime.
You must write code to handle exceptions when you call most asynchronous WebSocket network methods. Sometimes when an exception occurs, a network method can be retried to try and resolve the problem. Other times, your app may need to plan to continue without network connectivity using previously cached data.
The WebSocket APIs generally throw a single exception. Your exception handler can retrieve more detailed information on the cause of the exception to better understand the failure and make appropriate decisions.
The WebSocket APIs support two different methods for retrieving this detailed information on the cause of an exception.
The following sections discuss some specific scenarios for exception handling.
The constructor for the Windows.Foundation.Uri class used with the WebSocket APIs can throw an exception if the string passed is not a valid URI (contains characters that are not allowed in a URI). In C++, there is no method to try and parse a string to a URI. During development, the constructor for a Uri should be in a try/catch block to quickly find these types of errors. If an exception is thrown, the app can correct this issue quickly during development.
Your app should also check that the scheme in the URI is ws or wss since these are the only schemes supported by Windows.Networking.Sockets.MessageWebSocket or Windows.Networking.Sockets.StreamWebSocket.
Some example code to validate a string for a URI.
// Define some variables at the class level
Windows::Foundation::Uri^ resourceUri;
bool isUriValid = false;
///...
// If the value of 'inputUri' is set by the developer to a String
// and may contain errors.
// If we can't create a valid URI, we notify in a statusText variable
// about the incorrect input.
String ^uriString = inputUri;
try
{
isUriValid = false;
resourceUri = ref new Windows::Foundation:Uri(uriString);
if (resourceUri->SchemeName != "ws" && resourceUri->SchemeName != "wss")
{
statusText = "Only 'ws' and 'wss' schemes supported by WebSockets";
return;
}
isUriValid = true;
}
catch (InvalidArgumentException ^ex)
{
statusText = "The URI string was not valid";
return;
}
// ... continue with code to execute with a valid URI
The Windows.Networking.Sockets namespace has convenient helper methods and enumerations for handling errors when using WebSockets. This can be useful for handling specific network exceptions in your app.
An error encountered on a MessageWebSocket or StreamWebSocket operation results in an exception being thrown. The cause of the exception is an error value represented as an HRESULT value. The WebSocketError.GetStatus method is used to convert a network error from a WebSocket operation to a WebErrorStatus enumeration value. Most of the WebErrorStatus enumeration values correspond to an error returned by the system service for the operation. An app can filter on specific WebErrorStatus enumeration values to modify app behavior depending on the cause of the exception.
An example of code to handle exceptions when trying to make a connection with a MessageWebSocket.
using namespace concurrency;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Networking::Sockets;
// Define some variables at the class level
Windows::Foundation::Uri^ resourceUri;
bool isWebSocketConnected = false
bool retryWebSocketConnect = false;
// How many times have we tried to connect the socket
unsigned int retryConnectCount = 0;
// Maximum number of times to retry a connect operation
unsigned int maxRetryConnectCount = 5;
///...
resourceUri = Windows::Foundation::Uri(uriString);
action = ref new Windows::Foundation::IAsyncaction();
// we pass in a valid Uri parameter
MessageWebSocket ^ webSocket = ref new MessageWebSocket();
WebErrorStatus errorStatus;
HResult hr;
// Save the socket, so any subsequent steps can use it.
CoreApplication::Properties->Insert("clientWebSocket", webSocket);
// Connect to the remote server
create_task(webSocket->ConnectAsync(resourceUri)).then([this] (task<void> previousTask)
{
try
{
// Try getting all exceptions from the continuation chain above this point.
previousTask.get();
isWebSocketConnected = true;
// Mark the socket as connected. We do not really care about the value of the property, the mere
// existence means that we are connected.
CoreApplication::Properties->Insert("connected", nullptr);
}
catch (Exception^ ex)
{
hr = ex.HResult;
errorStatus = WebSocketError::GetStatus(hr);
if (errorStatus != Unknown)
{
switch (errorStatus)
{
case ServerUnreachable:
// Could be a connectivity problem
retryWebSocketConnect = true;
break;
case Timeout:
// Could be a connectivity problem
retryWebSocketConnect = true;
break;
case CannotConnect:
// The server might be temporarily busy
retryWebSocketConnect = true;
break;
case HostNameNotResolved:
// DNS servers may be down or could be a connectivity problem
retryWebSocketConnect = true;
break;
case ErrorHttpInvalidServerResponse:
// A serious server problem
retryWebSocketConnect = false;
break;
// handle other errors
// ...
default:
// Connection failed and no options are available
// Try to use cached data if available
// may want to tell user that connect failed
break;
}
}
}
else
{
// got an Hresult that is not mapped to an enum
// Could be a connectivity issue
retryWebSocketConnect = true;
}
}
});
}