我正在构建一个IRC客户端,并且想要一个只在客户端和服务器之间发送和接收线路的IRC独立网络类。发送行是可以的,但我对实现侦听器线程的好方法感到茫然。我尝试过的所有方法都有效,但有些瑕疵我无法解决。
我的第一种方法是使用StreamReader
作为基础创建一个名为_reader
的{{1}},然后只执行TcpClient.GetStream()
。这具有以下优点:后台侦听器线程自动停止,直到接收到一行。问题是,当我断开连接并停止监听器线程(或者应用程序刚刚退出)时,我不确定垃圾收集等有多好,等待_reader.ReadLine()
的线程就会被杀死。该线程是否会成为孤儿并以某种方式留在后台窃取资源?
我的第二种方法是基于相同的ReadLine()
创建名为NetworkStream
的{{1}},并创建一个循环来检查_reader
和TcpClient.GetStream()
循环如果它是假的,否则将字节推入一个StringBuilder,立即检查_reader.DataAvailable
并提取任何整行。这与ReadLine在数据检索上的效果相同,但我不喜欢常量continue
循环。在我的Core i5 CPU上,它不占用任何CPU,但在我更强大的i9笔记本电脑上它不断地窃取虚拟CPU。 \r\n
“解决”这个,但看起来像是一个肮脏的修复,互联网上的大量文章将其归类为代码气味。
那么什么是正确的解决方案?
答案 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)
如果是这样,那里的答案应该提供部分解决方案。基本上,它是一个提供返回Process(byte[])
的{{1}}方法的类,它保持部分内容的状态尚未形成完整的行。
使用它可以分开关注点。只需读取数据即可。每次读取一个块时,将其输入到该方法中,就会在完全形成线条时获得线条。