20

I am currently looking into using websockets to communicate between a client/agent and a server, and decided to look at C# for this purpose. Although I have worked with Websockets before and C# before, this is the first time I using both together. The first attempt used the following guide: http://www.codeproject.com/Articles/618032/Using-WebSocket-in-NET-Part

public static void Main(string[] args)
{
    Task t = Echo();
    t.Wait();
}

private static async Task Echo()
{
    using (ClientWebSocket ws = new ClientWebSocket())
    {
        Uri serverUri = new Uri("ws://localhost:49889/");
        await ws.ConnectAsync(serverUri, CancellationToken.None);
        while (ws.State == WebSocketState.Open)
        {
            Console.Write("Input message ('exit' to exit): ");
            string msg = Console.ReadLine();
            if (msg == "exit")
            {
                break;
            }
            ArraySegment<byte> bytesToSend = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));
            await ws.SendAsync(bytesToSend, WebSocketMessageType.Text, true, CancellationToken.None);
            ArraySegment<byte> bytesReceived = new ArraySegment<byte>(new byte[1024]);
            WebSocketReceiveResult result = await ws.ReceiveAsync(bytesReceived, CancellationToken.None);
            Console.WriteLine(Encoding.UTF8.GetString(bytesReceived.Array, 0, result.Count));
        }
    }
}

Although this seems to work as expected, I was wondering if there is any way I can use the built in streams/readers in .NET with ClientWebSocket?

Seems odd to me that Microsoft has this rich well established set of stream and reader classes, but then decides to implement ClientWebSocket with only the ability to read blocks of bytes that must be handled manually.

Lets say I wanted to transfer xml, it would be easy for me to just wrap the socket stream in a XmlTextReader, but this is not obvious with ClientWebSocket.

agnsaft
  • 1,791
  • 7
  • 30
  • 49
  • 1
    Start using SignalR – George Vovos Aug 17 '16 at 20:19
  • SignalR actually has tons of problems. It has always been very unreliable. Perhaps the biggest problem that I've had with it is that you cannot upgrade your server SignalR version without forcing clients to take an update. This is not always good business. – Steve Johnson Feb 11 '19 at 22:34

2 Answers2

3

Why don't work with byte arary? What about using XmlDictionaryReader.CreateTextReader which accept byte array from System.Runtime.Serialization assembly?. Working code:

namespace XmlReaderOnByteArray
{
    using System;
    using System.Xml;

    class Program
    {
        public static void Main(string[] args)
        {
            // Some XML
            string xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
                <note>
                <to>Tove</to>
                <from>Jani</from>
                <heading>Reminder</heading>
                <body>Don't forget me this weekend!</body>
                </note>";
            // Get buffer from string
            ArraySegment<byte> arraySegment = new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(xml));
            // Use XmlDictionaryReader.CreateTextReader to create reader on byte array
            using (var reader = XmlDictionaryReader.CreateTextReader(arraySegment.Array, new XmlDictionaryReaderQuotas())) {
                while (reader.Read()) {
                    Console.WriteLine("{0}[{1}] => {2}", reader.Name, reader.NodeType, reader.Value);
                }
            }
        }
    }
}
Artavazd Balayan
  • 2,353
  • 1
  • 16
  • 25
  • Why is websockets providing a arraysegment rather than byte [] or even complete message when the frame is complete? – agnsaft Aug 20 '16 at 17:56
  • 1
    As I understand, when await [ClientWebSocket.ReceiveAsync](https://msdn.microsoft.com/en-us/library/system.net.websockets.clientwebsocket.receiveasync(v=vs.110).aspx) return control, you will get full result (there are no chunking). In my opinion, the main benefit of ArraySegment is preventing copying of buffer. You can find more detailed answers [http://stackoverflow.com/q/4600024/5794617](http://stackoverflow.com/q/4600024/5794617) and [on msdn page](https://msdn.microsoft.com/en-us/library/1hsbd92d(v=vs.110).aspx#Anchor_7) – Artavazd Balayan Aug 20 '16 at 23:49
1

For network operations, someone has to do the work of waiting, checking, reading, waiting, understanding end of message, etc. Your example doesn't really do this, but just waits until 1024 bytes have been read and then stops.

You could implement an abstract Stream class to handle the loop which does the waiting and reading until "finished." Remember that a stream is just an abstraction of a byte-array. Also a Stream does not have the idea of asynchronous reading/writing so you would have to build that if you want it. The key here is that your Stream needs to understand the underlying bytes across the socket so that it knows when reading is finished.

matt-dot-net
  • 4,204
  • 21
  • 24
  • Does this mean I need to manually read each message block by block (e.g. into a memorystream) until I find the end of message and then handle the result? If so, wouldnt it be more convenient to have a built-in method to read the message to the end, instead of guessing size of receiving buffer? – agnsaft Aug 16 '16 at 22:55
  • How will anyone but you know when the end of message arrives? You are building the protocol here. – matt-dot-net Aug 18 '16 at 13:54
  • Fair enough, although since ClientWebsocket does know when a message is complete, I expected it to provide some convenience methods to provide the full message. I cant see a reason why it shouldnt, since I assume most people probably works with full messages anyway. – agnsaft Aug 20 '16 at 17:58
  • Actually, it looks like the abstract Stream [API](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.readasync?view=netframework-4.8) does have async read/write methods since at least .NET 4.5. – glopes Apr 08 '20 at 16:25