开发者

Have to read in the data twice? What am I missing?

开发者 https://www.devze.com 2023-04-05 20:10 出处:网络
Normally I can do something like this to fill up the byte array with stream data: byte[] dataLength = new byte[4];

Normally I can do something like this to fill up the byte array with stream data:

byte[] dataLength = new byte[4];

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

And that fills up the byte array.. However, I've been experimenting with async calls and my code looks like this:

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);
}

Which I feel is completely.. wrong. What is the point of the 开发者_JS百科async call if you just have to read it all over again in the callback synchronously? How can I access the already read-in bytes without making the dataLength a member variable of the class? Obviously I don't want to do that because there is more than one connection and they all have different values..

I feel like I'm missing something obvious here..


You do not have to read it all over again - when you call

clientStream.EndRead(async);

it returns the number of bytes that have been read, so you will want to do:

int bytesRead = clientStream.EndRead(async);

At this point your buffer has been filled with those bytes, reading from the stream in a synchronous fashion would just read more bytes.

If you do not want to make your buffer an instance variable you could use a closure with a delegate instead:

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);

Edit:

A better solution might be to put all the state in form of a custom class and pass it in 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);
        //..
    }
}


There don't appear to be full examples on MSDN (that I could find, NetworkStream.EndRead has some code though http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.endread(v=VS.90).aspx).

This tutorial has a full example though: http://www.switchonthecode.com/tutorials/csharp-tutorial-asynchronous-stream-operations

In short though, after you've called clientStream.EndRead, your original buffer dataLength should have been populated with the number of bytes returned by EndRead.

Also, I haven't tested it, but I would have though the subsequent call to Read would read in the next 4 bytes of the stream.

0

精彩评论

暂无评论...
验证码 换一张
取 消