views:

107

answers:

3

I try to use stream.DataAvailable to judge if it is finished,but sometimes the value is false but after a little while it is true again,i have to set a counter and judge the end by the symbol '>' like this

int connectCounter = 0;

while (connectCounter < 1200)
{
    if (stream.DataAvailable)
    {
        while (stream.DataAvailable)
        {
            byte[] buffer = new byte[bufferSize];
            int flag = stream.Read(buffer, 0, buffer.Length);
            string strReadXML_t = System.Text.Encoding.Default.GetString(buffer);
            strReadXML = strReadXML + strReadXML_t.Replace("\0", string.Empty);       
        }

        if (strReadXML.Substring(strReadXML.Length - 1, 1).Equals(">"))
        {
            break;
        }
    }
    Thread.Sleep(100);
    connectCounter++;
}

is there any good methord to deal with it?Thank you!

A: 

Try asynchronous reading.
When your callback is called, you can read the existing data buffer and then call BeginRead again. So that when somre more data is received, you callback will be called again.

Something like:

void DataReceived(IAsyncResult result) ///I am not sure about the parameters.
{
   ///read data from  buffer
   networkstream.BeginRead(
                 buffer, 0, buffer.Length,
                 new AsyncCallback(DataReceived),
                 null);
}

I think this is a pretty decent approach.

cornerback84
+1  A: 

You have a couple options. You can use a synchronous, blocking Read, or you can use an asynchronous IO pattern.

If you simply call stream.Read(), the call will block, and wait forever (until the TCP timeout), until data is available. It seems you don't want to do that. You want to wait, at most, 120 seconds (1200ms * 100), for the data to be completely read.

Something like this:

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

public void Run()
{
    TcpClient client ;//= ...
    NetworkStream networkStream = client.GetStream();
    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,
                                strReadXML = ""
                            });

    // do other stuff here. ...

    // finally, wait 120s for the reading to complete
    bool success = completedEvent.WaitOne(1200*100, false);
    if (!success)
    {
        client.Close();
    }
}


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

    // state.buffer now contains the bytes read

    string s = System.Text.Encoding.Default.GetString(state.b);
    state.strReadXML = state.strReadXML + s.Replace("\0", string.Empty);

    if (state.strReadXML.EndsWith(">"))
    {
        // got the "end".  signal completion
        state.e.Set();
        return;
    }

    // read again
    state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
}
Cheeso
Thanks!That helps me a lot!:)
Hwasin
There is still one problem in these codes.int n = state.ns.EndRead(ar); if (n == 0) { // no more bytes to read // signal completion state.e.Set(); return; } when n == 0,the methord return,but i can't simply judge there are no more bytes to read by n==0.because sometimes there is a little interrupt on the stream,and then it trans datas again.I don't know the Exact reason.Maybe the bandwith is a reason.
Hwasin
A: 

Do you have control over the sending application? If you do you can change the sending application to wrap the NetworkStream in a BinaryWriter and use .Write( string ) which will send the length of the string, and then the string itself. On the receiving application (as above) you can wrap the NetworkStream in a BinaryReader and call .ReadString() which will read the length from the stream, then read the correct length string in for you.

If you don't have control over the sending application then you can check the return from the stream.Read() call as it returns 0 at the end of the stream - though this will require the sending application to have closed the socket.

Also, stream.Read() is not guaranteed to return the number of bytes you requested, it could return less. Your flag variable should really be bytesread, and you should then pass this bytesread variable to the .GetString method.

Gareth Wilson