1

我正在构建一个 IRC 客户端,并且想要一个 IRC 独立网络类,它只在客户端和服务器之间发送和接收线路。发送线路是可以的,但我不知道实现监听器线程的好方法。我尝试过的所有方法都有效,但都有我无法解决的缺陷。

我的第一种方法是创建一个StreamReader被调用的_readerusingTcpClient.GetStream()作为基础,然后只做一个_reader.ReadLine(). 这样做的好处是后台侦听器线程会自动停止,直到收到一行。问题是,当我断开连接并停止侦听器线程(或应用程序刚刚退出)时,我不确定等待的线程ReadLine()被杀死对于垃圾收集等有多好。线程是否会成为孤立线程并以某种方式留在后台窃取资源?

我的第二种方法是创建一个基于相同的NetworkStream调用,并创建一个循环来检查它,如果它是错误的,则创建一个while循环,否则将字节推送到一个立即检查并提取任何整行的 StringBuilder 中。这在数据检索方面与 ReadLine 的效果相同,但我不喜欢不断循环。在我的 Core i5 CPU 上,它不需要任何 CPU,但在我功能更强大的 i9 笔记本电脑上,它会不断地窃取一个虚拟 CPU。“解决”了这个问题,但似乎是一个肮脏的修复,互联网上的许多文章将其归类为代码异味。_readerTcpClient.GetStream()_reader.DataAvailablecontinue\r\n_reader.DataAvailableThread.Sleep(1)

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

4

3 回答 3

1

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

列出大多数其他人,我不建议投票。我建议使用异步读写的解决方案,尽管它更难编程。

于 2011-04-27T16:56:36.817 回答
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 {};
}

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

于 2011-04-29T13:09:42.507 回答
0

老问题,这个问题应该早就解决了。无论如何,它看起来很像更老的问题c# - 处理基于行的网络 I/O 流的好方法是什么?- 堆栈溢出

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

使用它可以分离关注点。只需读取可用的数据。每次读取一个块时,将其输入该方法,您就会得到完全形成的行。

于 2015-08-19T08:40:17.813 回答