Есть ли известные шаблоны для асинхронного сетевого кода в C#?

Ваша строка импорта должна быть неправильной (из-за двойной кавычки). Ваша строка импорта должна выглядеть следующим образом:

import { OpaqueToken } from '@angular/core';

И OpaqueToken устарела в пользу InjectionToken, поэтому вы должны рассмотреть возможность обновления своего кода (см. https://github.com/angular /angular/issues/14763).

6
задан Mike Spross 15 March 2009 в 05:57
поделиться

4 ответа

Я должен был преодолеть подобные проблемы. Вот мое решение (изменено для установки собственному примеру).

Мы создаем обертку вокруг Stream (суперкласс NetworkStream, который является суперклассом TcpClient или безотносительно). Это контролирует чтения. Когда некоторые данные считаны, они буферизуются. Когда мы получаем индикатор длины (4 байта), мы проверяем, есть ли у нас полное сообщение (4 байта + длина тела сообщения). Когда мы делаем, мы повышаем a MessageReceived событие с телом сообщения, и удаляет сообщение из буфера. Эта техника автоматически обрабатывает фрагментированные сообщения и multiple-messages-per-packet ситуации.

public class MessageStream : IMessageStream, IDisposable
{
    public MessageStream(Stream stream)
    {
        if(stream == null)
            throw new ArgumentNullException("stream", "Stream must not be null");

        if(!stream.CanWrite || !stream.CanRead)
            throw new ArgumentException("Stream must be readable and writable", "stream");

        this.Stream = stream;
        this.readBuffer = new byte[512];
        messageBuffer = new List<byte>();
        stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), null);
    }

    // These belong to the ReadCallback thread only.
    private byte[] readBuffer;
    private List<byte> messageBuffer;

    private void ReadCallback(IAsyncResult result)
    {
        int bytesRead = Stream.EndRead(result);
        messageBuffer.AddRange(readBuffer.Take(bytesRead));

        if(messageBuffer.Count >= 4)
        {
            int length = BitConverter.ToInt32(messageBuffer.Take(4).ToArray(), 0);  // 4 bytes per int32

            // Keep buffering until we get a full message.

            if(messageBuffer.Count >= length + 4)
            {
                messageBuffer.Skip(4);
                OnMessageReceived(new MessageEventArgs(messageBuffer.Take(length)));
                messageBuffer.Skip(length);
            }
        }

        // FIXME below is kinda hacky (I don't know the proper way of doing things...)

        // Don't bother reading again.  We don't have stream access.
        if(disposed)
            return;

        try
        {
            Stream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(ReadCallback), null);
        }
        catch(ObjectDisposedException)
        {
            // DO NOTHING
            // Ends read loop.
        }
    }

    public Stream Stream
    {
        get;
        private set;
    }

    public event EventHandler<MessageEventArgs> MessageReceived;

    protected virtual void OnMessageReceived(MessageEventArgs e)
    {
        var messageReceived = MessageReceived;

        if(messageReceived != null)
            messageReceived(this, e);
    }

    public virtual void SendMessage(Message message)
    {
        // Have fun ...
    }

    // Dispose stuff here
}
2
ответ дан 17 December 2019 в 04:52
поделиться

Я думаю дизайн, который Вы использовали, прекрасен, это примерно, как я был бы и делать тот же вид вещи. Я не думаю, что Вы получили бы много путем рефакторинга в дополнительные классы/структуры и от того, что я видел, что Вы на самом деле сделали бы решение более сложным путем выполнения так.

Единственный комментарий, который я имел бы, относительно того, достаточно ли два чтения, где первой всегда является messgae длина и второе всегда быть телом, устойчивы. Я всегда опасаюсь подходов как этот, как будто они так или иначе выходят из синхронизации из-за непредвиденного обстоятельства (такого как другой конец, отправляющий неправильную длину), очень трудно восстановиться. Вместо этого я сделал бы единственное чтение с большим буфером так, чтобы я всегда получил все доступные данные из сети и затем осмотрел буфер для извлечения полных сообщений. Тот путь, если вещи действительно идут не так, как надо текущий буфер, может просто быть выброшен для возвращения вещей к чистому состоянию, и только текущие сообщения потеряны вместо того, чтобы остановить целый сервис.

На самом деле в данный момент у Вас была бы проблема, если бы Вы тело сообщения было большим и прибыло в два отдельных, получает, и следующее сообщение в строке отправило, это - длина в то же время, что и вторая половина предыдущего тела. Если бы это произошло, то Ваша длина сообщения закончилась бы добавленная к телу предыдущего сообщения, и Вы были в ситуации как desecribed в предыдущем абзаце.

1
ответ дан 17 December 2019 в 04:52
поделиться

Можно использовать yield return автоматизировать поколение конечного автомата для асинхронных обратных вызовов. Jeffrey Richter способствует этой технике через свой класс AsyncEnumerator, и я играл вокруг с идеей здесь.

1
ответ дан 17 December 2019 в 04:52
поделиться

Нет ничего неправильно со способом, которым Вы сделали это. Для меня, тем не менее, мне нравится разделять получение данных из обработки его, которая является тем, что Вы, кажется, думаете со своим предложенным классом MessageBuffer. Я обсудил это подробно здесь.

1
ответ дан 17 December 2019 в 04:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: