TcpClient 和 StreamReader 阻止读取

发布于 2024-08-16 07:11:45 字数 1861 浏览 6 评论 0原文

这是我的情况:

我正在编写一个聊天客户端来连接到聊天服务器。我使用 TcpClient 创建连接并从中获取 NetworkStream 对象。我使用 StreamReader 和 StreamWriter 来回读取和写入数据。

这是我的阅读内容:

public string Read()
{
 StringBuilder sb = new StringBuilder();
 try
 {
  int tmp;
  while (true)
  {                
   tmp = StreamReader.Read();
   if (tmp == 0)
    break;
   else
    sb.Append((char)tmp);
   Thread.Sleep(1);
  }
 }
 catch (Exception ex)
 {
  // log exception
 }
 return sb.ToString();
}

效果很好,而且很花哨。在我的主程序中,我创建了一个线程,不断调用此 Read 方法来查看是否有数据。下面是一个例子。

private void Listen()
{
 try
 {
  while (IsShuttingDown == false)
  {
   string data = Read();
   if (!string.IsNullOrEmpty(data))
   {
    // do stuff
   }
  }
 }
 catch (ThreadInterruptedException ex)
 {
  // log it
 }
}

...

Thread listenThread = new Thread(new ThreadStart(Listen));
listenThread.Start();

这很好用。当我想关闭应用程序时,问题就出现了。我收到来自UI的关闭命令,并告诉监听线程停止监听(即停止调用这个读取函数)。我调用 Join 并等待该子线程停止运行。就像这样:

// tell the thread to stop listening and wait for a sec
IsShuttingDown = true;            
Thread.Sleep(TimeSpan.FromSeconds(1.00));

// if we've reach here and the thread is still alive
// interrupt it and tell it to quit
if (listenThread.IsAlive)
    listenThread.Interrupt();

// wait until thread is done
listenThread.Join();

问题是它永远不会停止运行!我进入代码,监听线程被阻塞,因为 Read() 方法被阻塞。 Read() 只是坐在那里并且不返回。因此,线程永远没有机会休眠 1 毫秒然后被中断。

我确信如果我让它停留足够长的时间,我会收到另一个数据包,并有机会让线程休眠(如果它是一个活动的聊天室或从服务器获取 ping)。但我不想依赖于此。如果用户说关闭我想关闭它!

我发现的一种替代方法是使用 DataAvailable 方法,以便我可以在调用 StreamReader.Read() 之前检查它。这不起作用,因为它不可靠,并且我在从服务器读取数据包时丢失了数据。 (因为我无法正确登录,等等)

关于如何优雅地关闭这个线程有什么想法吗?我不想在监听线程上调用 Abort()...

Here's my situation:

I'm writing a chat client to connect to a chat server. I create the connection using a TcpClient and get a NetworkStream object from it. I use a StreamReader and StreamWriter to read and write data back and forth.

Here's what my read looks like:

public string Read()
{
 StringBuilder sb = new StringBuilder();
 try
 {
  int tmp;
  while (true)
  {                
   tmp = StreamReader.Read();
   if (tmp == 0)
    break;
   else
    sb.Append((char)tmp);
   Thread.Sleep(1);
  }
 }
 catch (Exception ex)
 {
  // log exception
 }
 return sb.ToString();
}

That works fine and dandy. In my main program I create a thread that continually calls this Read method to see if there is data. An example is below.

private void Listen()
{
 try
 {
  while (IsShuttingDown == false)
  {
   string data = Read();
   if (!string.IsNullOrEmpty(data))
   {
    // do stuff
   }
  }
 }
 catch (ThreadInterruptedException ex)
 {
  // log it
 }
}

...

Thread listenThread = new Thread(new ThreadStart(Listen));
listenThread.Start();

This works just fine. The problem comes when I want to shut down the application. I receive a shut down command from the UI, and tell the listening thread to stop listening (that is, stop calling this read function). I call Join and wait for this child thread to stop running. Like so:

// tell the thread to stop listening and wait for a sec
IsShuttingDown = true;            
Thread.Sleep(TimeSpan.FromSeconds(1.00));

// if we've reach here and the thread is still alive
// interrupt it and tell it to quit
if (listenThread.IsAlive)
    listenThread.Interrupt();

// wait until thread is done
listenThread.Join();

The problem is it never stops running! I stepped into the code and the listening thread is blocking because the Read() method is blocking. Read() just sits there and doesn't return. Hence, the thread never gets a chance to sleep that 1 millisecond and then get interrupted.

I'm sure if I let it sit long enough I'd get another packet and get a chance for the thread to sleep (if it's an active chatroom or a get a ping from the server). But I don't want to depend on that. If the user says shut down I want to shut it down!!

One alternative I found is to use the DataAvailable method of NetworkStream so that I could check it before I called StreamReader.Read(). This didn't work because it was undependable and I lost data when reading from packets from the server. (Because of that I wasn't able to login correctly, etc, etc)

Any ideas on how to shutdown this thread gracefully? I'd hate to call Abort() on the listening thread...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

纵性 2024-08-23 07:11:45

实际上,唯一的答案是停止使用Read并切换到使用异步操作(即BeginRead)。这是一个更难使用的模型,但意味着没有线程被阻塞(并且您不需要为每个客户端分配一个线程(一种非常昂贵的资源),即使客户端没有发送任何数据)。

顺便说一句,在并发代码中使用 Thread.Sleep 是一种不好的味道(在重构意义上),它通常表明更深层次的问题(在这种情况下,应该执行异步、非阻塞操作) 。

Really the only answer is to stop using Read and switch to using asynchronous operations (i.e. BeginRead). This is a harder model to work with, but means no thread is blocked (and you don't need to dedicate a thread—a very expensive resource—to each client even if the client is not sending any data).

By the way, using Thread.Sleep in concurrent code is a bad smell (in the Refactoring sense), it usually indicates deeper problems (in this case, should be doing asynchronous, non-blocking, operations).

黑凤梨 2024-08-23 07:11:45

您实际上是否使用 System.IO.StreamReader 和 System.IO.StreamWriter 从套接字发送和接收数据?我不知道这是可能的。我只在 TcpClientNetworkStream 对象上使用过 Read()Write() 方法> 的 GetStream() 方法。

假设这是可能的,当到达流末尾时,StreamReader 返回 -1,而不是 0。所以在我看来,您的 Read() 方法处于无限状态环形。

Are you actually using System.IO.StreamReader and System.IO.StreamWriter to send and receive data from the socket? I wasn't aware this was possible. I've only ever used the Read() and Write() methods on the NetworkStream object returned by the TcpClient's GetStream() method.

Assuming this is possible, StreamReader returns -1 when the end of the stream is reached, not 0. So it looks to me like your Read() method is in an infinite loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文