Приходится читать данные дважды? Что мне не хватает? - PullRequest
0 голосов
/ 20 сентября 2011

Обычно я могу сделать что-то вроде этого, чтобы заполнить массив байтов потоковыми данными:

byte[] dataLength = new byte[4];

clientStream.Read(dataLength, 0, dataLength.Length);

И это заполняет массив байтов ... Однако я экспериментировал с асинхронными вызовами и мойкод выглядит так:

byte[] dataLength = new byte[4];

clientStream.BeginRead(dataLength, 0, dataLength.Length, Read, clientStream);

private void Read(IAsyncResult async)
{
    NetworkStream clientStream = (NetworkStream)async.AsyncState;

    clientStream.EndRead(async);

    byte[] dataLength = new byte[4]; // ..?

    clientStream.Read(dataLength, 0, dataLength.Length); // Have to re-read in data with synchronous version?..

    int result = BitConverter.ToInt32(dataLength, 0);
}

Что я считаю совершенно .. неправильно.Какой смысл асинхронного вызова, если вам просто нужно прочитать все это снова в синхронном обратном вызове?Как я могу получить доступ к уже прочитанным байтам, не делая dataLength переменной-членом класса?Очевидно, я не хочу этого делать, потому что существует более одного соединения, и все они имеют разные значения ..

Мне кажется, что я упускаю что-то очевидное здесь ...

Ответы [ 2 ]

2 голосов
/ 20 сентября 2011

Вам не нужно читать все заново - при вызове

clientStream.EndRead(async);

возвращается количество прочитанных байтов, поэтому вам нужно будет сделать:

int bytesRead = clientStream.EndRead(async);

В этот момент ваш буфер заполнен этими байтами, синхронное чтение из потока будет просто читать больше байтов.

Если вы не хотите делать свой буфер переменной экземпляра, которую вы можете использоватьвместо этого закрытие с делегатом:

byte[] buffer = new byte[4];
clientStream.BeginRead(buffer, 0, buffer.Length, (IAsyncResult async) =>
{
    int bytesRead = clientStream.EndRead(async);
    if (bytesRead == 4)
    {
        int result = BitConverter.ToInt32(buffer, 0);
        //..
    }
}, clientStream);

Редактировать:

Лучшим решением может быть поместить все состояние в виде пользовательского класса и передать его вBeginRead():

public class StreamState
{
    public byte[] Buffer { get; set; }
    public NetworkStream Stream { get; set; }
}

clientStream.BeginRead(buffer, 0, buffer.Length, Read, new StreamState { Buffer = buffer, Stream = clientStream });


private void Read(IAsyncResult async)
{
    StreamState state = (StreamState) async.AsyncState;
    int bytesRead = state.Stream.EndRead(async);
    if (bytesRead == 4)
    {
        int result = BitConverter.ToInt32(state.Buffer, 0);
        //..
    }
}
0 голосов
/ 20 сентября 2011

Похоже, что на MSDN нет полных примеров (которые я мог бы найти, хотя NetworkStream.EndRead имеет некоторый код http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.endread(v=VS.90).aspx).

Этот учебник содержит полный пример: http://www.switchonthecode.com/tutorials/csharp-tutorial-asynchronous-stream-operations

Короче говоря, после того, как вы вызвали clientStream.EndRead, ваш первоначальный буфер dataLength должен был заполниться количеством байтов, возвращаемых EndRead.

Кроме того, я не проверялэто, но я хотел бы, хотя последующий вызов Read будет читать в следующих 4 байтах потока.

...