2011-04-27 71 views
1

我正在构建一个IRC客户端,并且需要一个IRC独立网络类,它只是在客户端和服务器之间发送和接收线路。发送行是确定的,但是我不知道如何实现一个监听器线程。我尝试过的所有方法都有效,但有缺陷,我无法解决。什么是实施基于行的网络工作者类的正确方法

我的第一个方法是创建一个StreamReader使用TcpClient.GetStream()为基础,只是在做一个叫_reader.ReadLine()_reader。这样做的好处是后台侦听器线程会自动停止,直到收到一条线。问题是,当我断开和停止监听器线程(或应用程序刚刚退出)时,我不确定它有多好,因为垃圾回收等,线程等待ReadLine()只是被杀死。该线程是否会成为孤儿,并以某种方式留在后台窃取资源?

我的第二个方法是创建基于相同TcpClient.GetStream()一个NetworkStream_reader,并创建一个循环,检查_reader.DataAvailablecontinue while循环,如果是假的,否则按字节到被立即检查\r\n一个StringBuilder并提取任何整行。这与ReadLine在数据检索方面的效果相同,但我不喜欢常量_reader.DataAvailable循环。在我的酷睿i5 CPU上,它不需要任何CPU,但是在我的功能更强大的i9笔记本电脑上,它不断窃取虚拟CPU。 Thread.Sleep(1)“解决”了这一点,但似乎是一个肮脏的修复,并在互联网上的许多文章将此分类为代码味道。

那么适当的解决方案是什么?

回答

1

如果你正在中止监听线程,那很糟糕。但是,如果应用程序退出(或者关闭底层套接字),那么它会很好。

列出大多数人,我不推荐投票。我会建议使用异步读取和写入的解决方案,但编程起来要困难得多。

+0

我已经看过异步方法,但MSDN文档 - 尤其是示例代码 - 充满了缺陷和错误。我还没有找到一个很好的指导如何使用它们。 – carlsb3rg 2011-04-27 18:43:31

+0

是的。最有用的资源是非托管TCP/IP应用程序。我在[常见问题](http://nitoprograms.blogspot.com/2009/04/tcpip-net-sockets-faq.html)的第2部分有一些指导原则,但它不是教程级材料。 – 2011-04-27 18:57:50

+0

我从不放弃线程。这是后台线程在应用程序关闭时自动死亡。侦听循环是'while(tcpClient.Connected){...}'。如果我使用'.ReadLine()',那么就没有办法强制程序继续执行下一个'while'检查并优雅地结束循环。这就是为什么我想使用NetworkStream方法,因为我可以在数据不可用时连续循环,并在需要时正常退出。我喜欢你的常见问题。你应该避开常见的FAQ条目。 – carlsb3rg 2011-04-27 20:57:30

0

这是让你去的东西。

请注意,构建消息时使用string效率不高。但它使一切更具可读性。

public abstract class LineBasedChannel 
{ 
    Socket _socket; 
    string _inbuffer = string.Empty; 
    Encoding _encoding; 
    byte[] _buffer = new byte[8192]; 

    public LineBasedChannel(Socket socket) 
    { 
     _socket = socket; 
     _encoding = Encoding.ASCII; 
     _sb = new StringBuilder(); 
    } 

    public void Start() 
    { 
     _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None,OnRead, null); 
    } 

    public void OnRead(IAsyncResult res) 
    { 
     var bytesRead = _socket.EndReceive(res); 
     if (bytesRead == 0) 
     { 
      HandleDisconnect(); 
      return; 
     } 

     _inbuffer += _encoding.GetString(_buffer, 0, bytesRead); 
     _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None,OnRead, null); 

     while (true) 
     { 
      int pos = _inbuffer.IndexOf("\r\n"); 
      if (pos == -1) 
       break; 

      OnReceivedLine(_inbuffer.SubString(0, pos+2); 
      _inbuffer = _inbuffer.Remove(0,pos+1); 
     } 
    } 

    protected abstract void OnReceivedLine(string line); 
} 

public class IrcTcpChannel : LineBasedChannel 
{ 
    protected override void OnReceivedLine(string line) 
    { 
     var cmd = new IrcCommand(); 
     cmd.Channel = line.SubString(x,y); 
     cmd.Message = line.SubString(z,a); 
     CommandReceived(this, new IrcCommandEventArgs(cmd)); 
    } 

    public event EventHandler<IrcCommandEventArgs> CommandReceived = delegate {}; 
} 

另请注意,我没有处理该代码中的任何异常,这是强制性的。异步方法中的任何未处理的异常将终止您的应用程序。

0

老问题,这个问题早就定下来了。无论如何,它看起来非常像旧的问题c# - What is a good method to handle line based network I/O streams? - Stack Overflow

如果是这样,那么答案应该提供部分解决方案。基本上,这是一个类,它提供了一个返回IEnumerable<string>Process(byte[])方法,该方法保持部分内容的状态尚未形成完整行。

使用这将允许分开关注。只读数据,因为它是可用的。每次读取块时,都会将它馈送到该方法中,因为它们完全形成时会出现线条。

相关问题