2

The code below works if there's not a lot of data from the server, or if there's a lot data and I uncomment the Thread.Sleep(500) line. I however don't want to use the Thread.Sleep line, but if I remove it, the program will finish before it read all the data from the server. It seems the problem is that EndRead() is not blocking until all the data are read, which understandable and should be fixed with the recursive calls to ReceiveCallback, but it's not.

I have a working client written using C/C++ on Linux. I'm trying to make it work with c# and .Net. I'm using MonoDevelop with .Net 4.0 on Linux. I appreciate any help on how to fix this.

cli.send("command");
// Thread.Sleep(500); 
cli.receive();
Console.WriteLine("response: {0}", cli.Response);
    private Static ManualResetEvent recvEvt = new ManualResetEvent(false);

    public static void receive() {
        StateObject state = new StateObject();
        try {
            state.stream = client.GetStream();
            state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
            recvEvt.WaitOne();
            recvEvt.Reset();
        }
        catch (ArgumentNullException ne) { errorMessage = ne.ToString(); }
        catch (ArgumentException ae) { errorMessage = ae.ToString(); }
        catch (ObjectDisposedException oe) { errorMessage = oe.ToString(); }
        catch (InvalidOperationException ie) { errorMessage = ie.ToString(); }
        catch (SocketException se) { errorMessage = se.ToString(); }
    }


   private static void ReceiveCallback(IAsyncResult ar) {
        StateObject state = (StateObject)ar.AsyncState;
        try {
            int bytesRead = state.stream.EndRead(ar);
            if (bytesRead > 0 ) {
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                response = state.sb.ToString();
                if (state.stream.DataAvailable) {
                    state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
                    recvEvt.WaitOne();
                    recvEvt.Reset();
                }else
                    recvEvt.Set();
            }else
                recvEvt.Set();
        }
        catch (ArgumentNullException ne) { errorMessage = ne.ToString(); }
        catch (ArgumentException ae) { errorMessage = ae.ToString(); }
        catch (ObjectDisposedException oe) { errorMessage = oe.ToString(); }
        catch (InvalidOperationException ie) { errorMessage = ie.ToString(); }
        catch (SocketException se) { errorMessage = se.ToString(); }
    }
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
user2238950
  • 21
  • 1
  • 3

1 Answers1

3

The problem is that there is no way for the application to know for sure that there is more data coming in over the wire. All it know is if it has received any data. What DataAvailable will tell you is if there is data that has been received that you can read from the network stream. Data that the remote end has not yet sent to you and data that it has sent but that has not yet been delivered to you over the network is essentially unknown to your application.

You will have to call BeginRead again repeatedly until either you or the remote end hangs up. Somewhat simplified ReceiveCallback would look something like this:

private static void ReceiveCallback(IAsyncResult ar) {
    StateObject state = (StateObject)ar.AsyncState;
    int bytesRead = state.stream.EndRead(ar);
    if (bytesRead > 0 ) {
        state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
        state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
    } else {
        response = state.sb.ToString();
    }
}

The callback will be called once data is received or the connection is terminated. The response is complete once the connection is closed.

  • 1
    Thanks. I already tried it, and it hangs. The code above is the only one I found works but requires `Thread.Sleep` for large data. I have spend a bit of time on this, reading through the MSDN documentation and trying different combinations but doesn't seems to go anywhere. – user2238950 Apr 03 '13 at 07:58
  • If it hangs then it sounds like the remote end still has the connection open but does not send more data. Either have the remote end close the connection or implement a protocol which the receiver can use in order to determine if the complete message has been received. –  Apr 03 '13 at 16:32
  • Thanks. That's fix it. I cannot modify the server but I realized it send a particular prompt after every command. I use it to indicate the end of input. – user2238950 Apr 03 '13 at 22:49