Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closing a NetworkStream after calling BeginRead() in C#

I have implemented a system that mimics the DataReceived event of a serial port, whereby the reading of data from a TCPClient object's NetworkStream is triggered by using the BeginRead() method as follows:

TcpClient server = new TcpClient();
server.Connect(IPAddress.Parse(ip), 10001);
server.GetStream().BeginRead(buffer, 0, buffer.Length, new AsyncCallback(DataReceived), server.GetStream());

which calls the following method from another thread:

 private void DataReceived(IAsyncResult result)
    {
        res = result;
        server.GetStream().EndRead(result);

        //append received data to the string buffer
        stringBuffer += System.Text.ASCIIEncoding.ASCII.GetString(buffer);

        //clear the byte array
        Array.Clear(buffer, 0, buffer.Length);

        //trigger the parser
        waitHandle.Set();

        server.GetStream().BeginRead(buffer, 0, buffer.Length, new AsyncCallback(DataReceived), buffer);
    }

This appears to work correctly. I can send and receive data to a device on the network without issue. However, when I attempt to disconnect using the following method, the program crashes:

public override void disconnect()
{
    server.Close();
}

It throws the following error:

A first chance exception of type 'System.ObjectDisposedException' occurred in System.dll

I have also tried implementing the disconnect method as follows:

server.GetStream().Close();

but this results in the following error:

A first chance exception of type 'System.InvalidOperationException' occurred in System.dll

I assume this has something to do with the fact that the BeginRead() method has been called and the EndRead() method has not. If that is the case how can I close the stream without it crashing?

like image 983
isometrik Avatar asked Aug 17 '11 13:08

isometrik


1 Answers

I would call GetStream just once and store the result somewhere and use that for accessing the stream.

Stream nstrm = server.GetStream();

Use nstrm for all accesses to the NetworkStream...

safest way would be to maintain a flag for closing down and just setting that flag in disconnect().

In DataReceived you would directly after EndRead check for that flag and if it is set do this:

server.Close();
nstrm.Close();

see http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.getstream.aspx

EDIT - as per comment:

if (flag2Close)
{
    server.Close();
    nstrm.Close();
    flag2Close = false;
}
else
{
    nstrm.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(DataReceived), buffer);
}

BTW: for production code it needs some exception handling etc.

like image 187
Yahia Avatar answered Oct 29 '22 00:10

Yahia