views:

347

answers:

1

Hi,

I'm using a TcpClient in one of my Compact Framework 2.0 applications. I want to receive some information from a TCP server.

As the Compact Framework does not support the timeout mechanisms of the "large" framework, I'm trying to implement my own timeout-thing. Basically, I want to do the following:

IAsyncResult result = networkStream.BeginRead(buffer, 0, size, ..., networkStream);
if (!result.AsyncWaitHandle.WaitOne(5000, false))
  // Handle timeout


private void ReceiveFinished(IAsyncResult ar)
{
  NetworkStream stream = (NetworkStream)ar.AsyncState;
  int numBytes = stream.EndRead(ar);

  // SIGNAL IASYNCRESULT.ASYNCWAITHANDLE HERE ... HOW??
}

I'd like to call Set for the IAsyncResult.AsyncWaitHandle, but it doesn't have such a method and I don't know which implementation to cast it to.

How do I set the wait handle? Or is it automatically set by calling EndRead? The documentation suggests that I'd have to call Set myself...

Thanks for any help!

UPDATE
Seems that the wait handle is set automatically when calling EndRead - but it's not in the docs. Can somebody confirm this?

UPDATE 2
Wrote client.BeginRead in my sample code. Of course, BeginRead is called on the NetworkStream...

+2  A: 

I think you have a misunderstanding about async IO with TCP.

To kick off async IO, call stream.BeginRead().
In the callback, you call EndRead on the stream.

You don't call BeginRead on the TcpClient, as your code shows. Your app doesn't ever signal the WaitHandle. The IO layer will invoke your callback when the waithandle is signalled, in other words when the async Read happens.

In your callback, normally you'd call BeginRead again, on the stream, if it's possible that you'll be receiving more data.

You can see a clear example in this answer.

Before starting the BeginRead/EndRead dance, you may want to do an async Connect on the TcpClient - then you would use BeginConnect. But that's done just once. Alternatively, you might want a synchronous connect, in which case you just call TcpClient.Connect().

example code:

    private class AsyncState
    {
        public NetworkStream ns;
        public ManualResetEvent e;
        public byte[] b;
    }

    public void Run()
    {
        NetworkStream networkStream = ...;
        byte[] buffer = new byte[1024];

        var completedEvent = new ManualResetEvent(false);

        networkStream.BeginRead(buffer, 0, buffer.Length,
                                AsyncRead,
                                new AsyncState
                                {
                                    b = buffer,
                                    ns = networkStream,
                                    e = completedEvent
                                });

        // do other stuff here. ...

        // finally, wait for the reading to complete
        completedEvent.WaitOne();
    }


    private void AsyncRead(IAsyncResult ar)
    {
        AsyncState state = ar as AsyncState;
        int n = state.ns.EndRead(ar);
        if (n == 0)
        {
            // signal completion
            state.e.Set();
            return;
        }

        // state.buffer now contains the bytes read
        // do something with it here...
        // for example, dump it into a filesystem file. 

        // read again
        state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
    }
Cheeso
Hi, of course you're right - that's what I'm doing. I'm actually starting the read on the stream (updated code sample above). Question remains: Is the WaitHandle signalled automatically? The docs specifically state that to wait for the async read to be finished, one should wait for the handle to be set. I assume now that the handle is set through EndRead implicitly, but can you confirm this?
Thorsten Dittmar
I checked the documentation, and found the part that said "call WaitOne() in the main thread and call Set() in the callback." The doc is poorly written. That bit refers to a separate waithandle - like a ManualResetEvent() - that can be used to synchronize the cooperating threads. The sample code shows a separate synchronization structure (`allDone`); unfortunately it is partial code and does not show the initialization or declaration of that variable, so we are left to assume. But it's clear it is not the IAsyncResult. The bottom line: You do not need to call Set() on the IAsyncResult.
Cheeso
Thanks for that - this is exactly what I'm doing in my code. Shows me I'm doing it right :-)
Thorsten Dittmar