With the C++ REST SDK 2.1 release, we are excited to announce support for client side WebSockets across multiple platforms (Linux, OSX, iOS, Windows Desktop and Store). This is a relatively new protocol, providing full duplex communication over TCP. For a detailed specification, refer to the RFC 6455.
Why use WebSockets
WebSocket comes in handy while writing an application that requires low-latency, bidirectional communication between a client and server. Some examples where this is applicable are collaborative editing applications, multi player games or applications that require real time updates. Consider a stock ticker application, where the client registers with the server to monitor the stock quotes for different companies. Using HTTP in such scenarios would require the client to poll for requests every few seconds to fetch the updates. Also, every request response will be accompanied with some headers that take up network bandwidth. Using WebSockets here solves these issues: When the server sees any movements in the stock, it sends updates to the client (low-latency). Client does not have to send any “check updates?” request to the server. When the client wants to listen to other stock quotes, it can register for updates (bidirectional).
WebSocket client implementation is under the web::experimental::web_sockets::client namespace of the C++ REST SDK. To reference the SDK from your project, refer to our documentation on codeplex. In the sections below, we will walk through how to create and perform websocket client operations:
Connecting to the server
This involves an opening handshake between the client and the server, which is nothing but an HTTP upgrade request with some WebSocket protocol specific header exchanges (Sec-WebSocket-Key, Sec-WebSocket-Version). Upon receiving the expected status code from the server, the connection is established.
The websocket_client::connect() API takes care of this handshake. It returns a task which completes when the connection is set up:
Sending and receiving messages
websocket_client::send() and websocket_client::receive() APIs can be used for sending and receiving messages. The library supports both text and binary messages.
websocket_outgoing_message represents a message to be sent.
The websocket_outgoing_message::set_utf8_message API (with both string and stream overloads) can be used to construct a text message. Note: C++ REST SDK will not validate if the data is actually UTF-8 encoded or not.
Similarly, the websocket_outgoing_message::set_binary_message interface can be used to construct a binary message.
Finally call the websocket_client::send(websocket_outgoing_message msg) API to send the message over to the server. This returns a task which is completed when the message has been handed over to the underlying TCP layer.
To receive messages, call the websocket_client::receive() API. This returns a task that is completed when a message is received by the client endpoint. websocket_incoming_message represents the received message. You can read the message data using the extract_string() (for UTF-8 message type) or the body() (for both UTF-8 or binary message types) interfaces on the received message.
Note: Calling receive() multiple times will initiate pending receives and queue them up. Upon receiving a message, only the first receive will complete.
Closing the connection
The closing handshake involves the client sending and receiving a close control frame.
The websocket_client::close() API closes the websocket connection.
Demo (Text chat application)
Now let us walk through a text chat application which demonstrates the use of WebSockets.
Using the sample
The sample consists of chat client which is a Windows store application and a chat server which is a console application. First build the ChatServer project in the solution to generate CharServer.exe. Open a command prompt and run ChatServer.exe to start the chat server. Following screen shot shows the running ChatServer.exe .
Next, build and deploy the ChatClient project and launch the deployed app. To use the chat application you will need two instances of chat client. Since windows store apps can only have a single instance on a machine, you will need to deploy the app to some other machine a well (or as a hacky alternative, you can change the identity of the app in package.appxmanifest file and deploy it under a different identity). Note that before you build and deploy the ChatClient, you will have to change the CHAT_SERVER_URL macro (in file ProjectUtilities.h under folder ChatClientServer\Common) depending on where you are running the CharServer.exe. Following screen shot shows the ChatClient after it is launched.
Enter your screen name and click connect. When the second client will connect both the first and second client will be able to see each other. To send the text message, select the online user on the left panel and enter the text in the chat text window and click send to send the message. Following screen shot shows the chat conversation between two chat clients.
The chat server is written using POCO C++ Library and uses HTTP upgrade mechanism to upgrade to WebSocket protocol. The focus of this text chat app is to demonstrate the use of client-side WebSocket support added in C++ REST SDK. Hence we will skip the details of chat server code. The chat server is there because the application requires it.
The chat client is a C++ windows store app and uses C++/CX and XAML for UI. The class ClientManager (see file ClientManager.h and ClientManager.cpp under folder ChatClientServer\ChatClient) is main part of the app. It talks with the chat server and app UI (see file MainPage.xaml.h and MainPage.xaml.cpp under folder ChatClientServer\ChatClient) and acts as a bridge between the two. The chat client uses JSON to represent data exchanged between the chat client and chat server (see file ServerClientPackets.h and ServerClientPackets.cpp under folder ChatClientServer\Common).
After launching the chat client, when the user clicks the connect button, the app establishes a web socket connection with the chat server (see function connectButton_Click(…) in file MainPage.xaml.cpp) and starts listening to messages from the server (see function listen_to_incoming_message() in file ClientManager.cpp). The client then sends user-information to the chat server. When the chat server receives user information, it sends back list of users already connected to the chat server (see function connect() in file ClientManager.cpp).
The function listen_to_incoming_message() uses asynchronous loop to listen for messages from chat server. Each iteration (which is a pplx::task<>) of the asynchronous loop processes one messages from the chat server. After the message is processed, the current iteration signals its completion and a task for next iteration is created (see functions async_do_while(…), _do_while_iteration(…) and _do_while_impl(…) in file ProjectUtilities.cpp under folder ChatClientServer\Common).
When the user enters the chat text and clicks send button, the chat client uses the web socket connection to send the message (see function send_chat_text(…) in file ClientManager.cpp). When the user want to disconnect from chat server and clicks disconnect button, the chat client first sends message to server telling it wants to log out and then closes the web socket connection (see function disconnect() in file ClientManger.cpp).
You can download the attached zipped file containing the sample and play with it. Please note that the sample code in the “.zip” is released under Apache 2.0 license. You will need Visual Studio 2013 to build the sample. The sample fetches the POCO dependencies using the “Fix8 POCO dependency” NuGet package. We would love to hear your comments below.
Hasibur Rahman and Kavya Kotacherry,
Visual C++ Libraries Team.