使用C# ClientWebSocket与流

20

我目前正在研究使用Websockets在客户端/代理和服务器之间通信,并决定使用C#来实现。虽然我以前曾经使用过Websockets和C#,但这是我第一次将它们结合起来使用。第一次尝试使用了以下指南: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));
        }
    }
}

虽然这似乎符合预期,但我想知道是否有任何方法可以使用.NET中内置的流/读取器与ClientWebSocket一起使用?
对我来说,微软拥有丰富的、经过验证的流和读取器类集合,但是却决定仅实现具有必须手动处理的字节块读取能力的ClientWebSocket,这看起来很奇怪。
假设我想传输XML,那么我只需将套接字流包装在XmlTextReader中即可,但是使用ClientWebSocket并不明显。

1
开始使用SignalR。 - George Vovos
SignalR实际上存在很多问题。它一直非常不可靠。我遇到的最大问题可能是,你无法升级服务器上的SignalR版本而不强制客户端进行更新。这并不总是好的商业做法。 - Steve Johnson
2个回答

3
为什么不使用字节数组?考虑使用System.Runtime.Serialization程序集中的XmlDictionaryReader.CreateTextReader,该方法接受字节数组作为参数。工作代码
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);
                }
            }
        }
    }
}

为什么在WebSocket中,当帧完成时,提供的是ArraySegment而不是byte[]或完整消息呢? - agnsaft
1
据我所知,当等待 ClientWebSocket.ReceiveAsync 返回控制权时,您将获得完整的结果(没有分块)。在我看来,ArraySegment 的主要优点是防止缓冲区复制。您可以在 https://dev59.com/BG445IYBdhLWcg3w_PG3 和 msdn 页面上找到更详细的答案。 - Artavazd Balayan

1
针对网络操作,必须有人负责等待、检查、读取、理解信息结束等工作。你的示例并没有真正做到这一点,只是等待读取了1024个字节就停止了。
你可以实现一个抽象的Stream类来处理循环等待和读取直到“完成”的过程。请记住,流只是字节数组的抽象表示。此外,流没有异步读写的概念,因此如果需要,您必须自行构建。关键在于,您的Stream需要理解套接字底层的字节,以便它知道何时读取结束。

这是否意味着我需要手动逐块阅读每个消息(例如,读入到MemoryStream中),直到找到消息的结尾,然后处理结果?如果是这样,那么有没有内置方法可以读取整个消息,而不是猜测接收缓冲区的大小,这样会更方便? - agnsaft
除了你以外,谁会知道消息何时结束?你正在构建协议。 - matt-dot-net
说得对,虽然ClientWebsocket知道何时消息完成,但我期望它提供一些方便的方法来提供完整的消息。我看不出为什么它不应该这样做,因为我认为大多数人可能都使用完整的消息。 - agnsaft
实际上,看起来自至少.NET 4.5以来,抽象的Stream API确实具有异步读/写方法。 - glopes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接