实现基于行的网络工作者类的正确方法是什么

时间:2011-04-27 08:51:27

标签: c# irc tcpclient networkstream

我正在构建一个IRC客户端,并且想要一个只在客户端和服务器之间发送和接收线路的IRC独立网络类。发送行是可以的,但我对实现侦听器线程的好方法感到茫然。我尝试过的所有方法都有效,但有些瑕疵我无法解决。

我的第一种方法是使用StreamReader作为基础创建一个名为_reader的{​​{1}},然后只执行TcpClient.GetStream()。这具有以下优点:后台侦听器线程自动停止,直到接收到一行。问题是,当我断开连接并停止监听器线程(或者应用程序刚刚退出)时,我不确定垃圾收集等有多好,等待_reader.ReadLine()的线程就会被杀死。该线程是否会成为孤儿并以某种方式留在后台窃取资源?

我的第二种方法是基于相同的ReadLine()创建名为NetworkStream的{​​{1}},并创建一个循环来检查_readerTcpClient.GetStream()循环如果它是假的,否则将字节推入一个StringBuilder,立即检查_reader.DataAvailable并提取任何整行。这与ReadLine在数据检索上的效果相同,但我不喜欢常量continue循环。在我的Core i5 CPU上,它不占用任何CPU,但在我更强大的i9笔记本电脑上它不断地窃取虚拟CPU。 \r\n“解决”这个,但看起来像是一个肮脏的修复,互联网上的大量文章将其归类为代码气味。

那么什么是正确的解决方案?

3 个答案:

答案 0 :(得分:1)

如果您正在中止侦听器线程,那就不好了。但是如果应用程序退出(或者如果你关闭底层套接字),那么它就可以了。

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

答案 1 :(得分: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 {};
}

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

答案 2 :(得分:0)

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

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

使用它可以分开关注点。只需读取数据即可。每次读取一个块时,将其输入到该方法中,就会在完全形成线条时获得线条。