Reputation: 21
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(); }
}
Upvotes: 2
Views: 2668
Reputation:
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.
Upvotes: 3