Part 1 gives an overview of the WebSocket protocol and .NET WebSocket support.Part 2 demonstrates how to use WebSocket in traditional ASP.NET and MVC 4 webapplications.
In this article, I will demonstrate how to use WCF to host a WebSocket service and writea client application. I will also show how to communicate with a WCF WebSocketservice usingJavaScript in a webpage.
To enable WebSocket on the server side, please refer to Part 1.
In WCF 4.5, netHttpBinding
and netHttpsBinding
have been re-implemented to support WebSocket. And to make a WCF service serve the client through WebSocket, we should set CallbackContract
for theServiceContract
attribute in the service interface declaration. For example:
[ServiceContract(CallbackContract = typeof(IWSChatCallback))]
public interface IWSChatService
{
[OperationContract(IsOneWay = true)]
Task SendMessageToServer(string msg);
}
The SendMessageToServer
method will be called by the client and themsg
argument is the message sent by the user. So the server should implement this method to handle the msg
received from the client.IsOneWay
should be set to true for the OperationContract
attribute because the client should not wait for the return value of the service method call in duplex communication. CallbackContract
is bound to IWSChatCallback
which is declared as below:
[ServiceContract]
interface IWSChatCallback
{
[OperationContract(IsOneWay = true)]
Task SendMessageToClient(string msg);
}
The SendMessageToClient
method will be called by the server and the msg
argument is the message sent by the server. So the client should implement this method to handle the msg
received from the server. Again,IsOneWay
should be set to true because the server should not wait for the return value of the callback method call in duplex communication.
Let’s simply implement IWSChatService
as an echo server:
public class WSChatService : IWSChatService
{
public async Task SendMessageToServer(string msg)
{
var callback = OperationContext.Current.GetCallbackChannel();
if (((IChannel)callback).State == CommunicationState.Opened)
{
await callback.SendMessageToClient(
string.Format("Got message {0} at {1}",
msg, DateTime.Now.ToLongTimeString()));
}
}
}
The service just decorates the message received from the client and calls SendMessageToClient
to send the decorated message to the client.
Then we need to modify web.config to add a protocol mapping schema in the
tag like:
The default netHttpBinding
and netHttpsBinding
will transfer data in Binary SOAP format. If you want to useText SOAP format, you could create a customized configuration and set messageEncoding
to “Text
” like:
OK. The WCF WebSocket service gets ready after I build it.
To write a WCF client application, I create a console application and add the previous WCF service to Service References. Then I implement theIWSChatServiceCallback
interface declared previously on the server:
internal class CallbackClient : IWSChatServiceCallback
{
public void SendMessageToClient(string msg)
{
Console.WriteLine(msg);
}
}
In the SendMessageToClient
method, I just display the message received from the server.
The program’s main function looks like:
class Program
{
static void Main(string[] args)
{
var context = new InstanceContext(new CallbackClient());
var client = new WSChatServiceClient(context);
while (true)
{
Console.Write("Input (\"Exit\" to exit):");
string input = Console.ReadLine();
if (input.ToUpperInvariant() == "EXIT")
{
break;
}
client.SendMessageToServer(input);
Thread.Sleep(500);
}
client.Close();
}
}
Again to emphasize, the client application can only work on Windows 8,Windows Server 2012, and above.
By using netHttpBinding
and netHttpsBinding
, all data transferred in a WCF WebSocket connection is in SOAP format (Binary or Text). To communicate with the WCF WebSocket service with JavaScript code in the web page, I have to parse the SOAP by myself.
To avoid SOAP, I need to create and use a custom binding as below:
I use byteStreamMessageEncoding
to specify the data should be transferred as byte stream instead of SOAP. In webSocketSettings
,transportUsage
is set to Always
to enable the WebSocket protocol regardless of the contract; createNotificationOnConnection
must be set to “true
” to notify the client when connection is established.
The service interface and implementation need to change as well. I modified IWSChatService
and IWSChatCallback
as below:
[ServiceContract(CallbackContract = typeof(IWSChatCallback))]
public interface IWSChatService
{
[OperationContract(IsOneWay = true, Action = "*")]
Task SendMessageToServer(Message msg);
}
[ServiceContract]
interface IWSChatCallback
{
[OperationContract(IsOneWay = true, Action="*")]
Task SendMessageToClient(Message msg);
}
I change the type of the msg
argument from string to Message
(System.ServiceModel.Channels
) which will wrap my UTF-8 encoded text.Then I re-implement IWSChatService
as below:
public class WSChatService : IWSChatService
{
public async Task SendMessageToServer(Message msg)
{
var callback = OperationContext.Current.GetCallbackChannel();
if (msg.IsEmpty || ((IChannel)callback).State != CommunicationState.Opened)
{
return;
}
byte[] body = msg.GetBody();
string msgTextFromClient = Encoding.UTF8.GetString(body);
string msgTextToClient = string.Format(
"Got message {0} at {1}",
msgTextFromClient,
DateTime.Now.ToLongTimeString());
await callback.SendMessageToClient(
CreateMessage(msgTextToClient));
}
private Message CreateMessage(string msgText)
{
Message msg = ByteStreamMessage.CreateMessage(
new ArraySegment(Encoding.UTF8.GetBytes(msgText)));
msg.Properties["WebSocketMessageProperty"] =
new WebSocketMessageProperty
{ MessageType = WebSocketMessageType.Text };
return msg;
}
}
The client side need not implement IWSChatCallback
. My JavaScript code is very similar to the example in Part 2:
WebSocket Chat
(display)
Next in Part 4, I will demonstrate how to use Microsoft.WebSockets.dll.
Using WebSocket in .NET 4.5: