Unblock NetworkStream.Read from another Thread?

D

DaveR

I have a background thread to accept and process TCP/IP connections.
When the application shuts down I want to gracefully terminate it.

The thread will block in NetworkStream.Read() waiting for data. I
expected that shutting down the TcpListener would unblock the Read()
and cause an exception, or return 0 bytes. But it does not work.

Relevant parts of the code are shown below. If a client connects, the
stream blocks in its Read() method. If Stop() is then called from the
main thread, NetworkStream.Read() stays blocked even though the
TcpListener has stopped (and thus the underlying socket is closed).

I expect Read() to throw an IOException with an inner SocketException,
or at least for it to unblock and return 0 bytes. But it doesn't work.

Any suggestions would be appreciated.

private TcpListener _listener;
public void StartListening()
{
_listener = new TcpListener(endPoint);
_listener.Start();
Thread thread = new Thread(new ThreadStart(this.Process));
thread.Start();
}

public void StopListening()
{
_listener.Stop();
}

private void Process()
{
while (true)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
NetworkStream strm = client.GetStream();
while (true)
{
int bytesRead = strm.Read(buf, 0, buf.Length);
// ... process ...
}
}
catch (IOException e)
{
// Expect a SocketException when the listener stops
}
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Dave,

I don't understand why you are looping infinitely. Why not use the
BeginAcceptTcpClient and BeginRead methods to perform your work? You
wouldn't have to block a thread and poll. You would just be notified when
the client is available, and when your read was complete.

It would also use less resources, since you would be using I/O
completion ports (under the wrappers) instead of tying up a thread.

Hope this helps.
 
G

Goran Sliskovic

DaveR said:
I have a background thread to accept and process TCP/IP connections.
When the application shuts down I want to gracefully terminate it.

The thread will block in NetworkStream.Read() waiting for data. I
expected that shutting down the TcpListener would unblock the Read()
and cause an exception, or return 0 bytes. But it does not work. ....
private void Process()
{
while (true)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
NetworkStream strm = client.GetStream();
while (true)
{
int bytesRead = strm.Read(buf, 0, buf.Length);
// ... process ...
}
}
catch (IOException e)
{
// Expect a SocketException when the listener stops
}
}
}

First, you are blocking accept while processing. When accept returns, you
should start new thread and process accepted connection on that thread. In
current implementation, you will not be able to process more than one
connection at the time.

To stop blocking read on TcpClient, you have to close tcpclient returned
from Accept, not listener. These two are unrelated. For that, you'll
probably have to track accepted connection (and remove them when they
disconnect).

Goran
 
W

William Stacey [MVP]

The listener socket is seperate from the accepted socket (i.e. two different
objects). Stopping the listener, just stops the listener, not all
connected sockets. To shutdown connected sockets, you would need to walk a
list and close each one. hth

--
William Stacey [MVP]

|I have a background thread to accept and process TCP/IP connections.
| When the application shuts down I want to gracefully terminate it.
|
| The thread will block in NetworkStream.Read() waiting for data. I
| expected that shutting down the TcpListener would unblock the Read()
| and cause an exception, or return 0 bytes. But it does not work.
|
| Relevant parts of the code are shown below. If a client connects, the
| stream blocks in its Read() method. If Stop() is then called from the
| main thread, NetworkStream.Read() stays blocked even though the
| TcpListener has stopped (and thus the underlying socket is closed).
|
| I expect Read() to throw an IOException with an inner SocketException,
| or at least for it to unblock and return 0 bytes. But it doesn't work.
|
| Any suggestions would be appreciated.
|
| private TcpListener _listener;
| public void StartListening()
| {
| _listener = new TcpListener(endPoint);
| _listener.Start();
| Thread thread = new Thread(new ThreadStart(this.Process));
| thread.Start();
| }
|
| public void StopListening()
| {
| _listener.Stop();
| }
|
| private void Process()
| {
| while (true)
| {
| try
| {
| TcpClient client = _listener.AcceptTcpClient();
| NetworkStream strm = client.GetStream();
| while (true)
| {
| int bytesRead = strm.Read(buf, 0, buf.Length);
| // ... process ...
| }
| }
| catch (IOException e)
| {
| // Expect a SocketException when the listener stops
| }
| }
| }
 
D

DaveR

I don't understand why you are looping infinitely. Why not use the
BeginAcceptTcpClient and BeginRead methods to perform your work?

I have used that before in a server that needed to process many
simultaneous connections, but in this case we need to handle only a
single connection at a time, so I thought the asynchronous operation
may be overkill. Maybe that was not a wise choice.

In any case, I solved the problem by making the TcpClient a member and
closing it from the main thread, which effectively unblocks the
NetworkStream.Read as I wanted.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top