我基本上试图使用C#,WinForms和TcpClient创建一个准系统IRC客户端,它将从irc服务器显示原始数据到文本区域(textbox1)。但是我正在努力更新代码(从服务器读取流)。现在我有一个运行一个函数(listener
)的计时器,它每100毫秒从TCP流中读取一次。然而,我的应用程序冻结,光标消失,应用程序挂起试图获取更多数据。那么什么是更好的更新功能?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net.Sockets;
namespace LogernIRC
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Variables
TcpClient client;
StreamReader sr;
StreamWriter sw;
//Functions
public void connect(string host)
{
client = new TcpClient(host, 6667);
sr = new StreamReader(client.GetStream());
sw = new StreamWriter(client.GetStream());
}
public void write(string str)
{
textBox1.Text += str;
}
public void sendData(string str)
{
sw.WriteLine(str);
sw.Flush();
}
public void listener()
{
string data = sr.ReadLine();
write(data);
}
//End Functions
private void Form1_Load(object sender, EventArgs e)
{
//Initialize
write("Welcome to LogernIRC. Type \"/help\" for help with commands.\r\n");
}
private void button1_Click(object sender, EventArgs e) //Submit button clicked
{
//TextBox 1 is the text area , textbox 2 is the message/command area
//Command Area
if (textBox2.Text == "/help")
{
write("Help:\r\n/connect Connect to IRC server\r\n/help Display this help menu\r\n/join Join channel");
}
if (textBox2.Text.StartsWith("/connect"))
{
write("\r\nConnecting to " + textBox2.Text.Split(' ')[1] + " on port 6667...");
connect(textBox2.Text.Split(' ')[1]);
}
if (textBox2.Text.StartsWith("/join"))
{
write("\r\nJoining channel " + textBox2.Text.Split(' ')[1]);
}
if (textBox2.Text == "/test")
{
timer1.Start();
connect("irc.freenode.net");
write("\r\nActivating test function...");
sendData("NICK Logern");
sendData("USER Logern 0 * :LOGERN");
listener();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
//Read Data
listener();
}
}
}
答案 0 :(得分:1)
它没有运行异步,对吧?因此UI将锁定直到循环完成。你永远在循环,对吧?这对于IRC机器人/客户来说非常普遍;我自己做过。
如果是这样,如果你使用的是.NET 4.0及以上版本,你可以试试这个:
await Task.Run(()=> { CodeThatLoopsForever(); });
让我试着更好地解释一下。让我们举个例子说你有这样的函数:
private void Connect()
{
while (true)
{
// Do socket listening
}
}
你可以通过点击按钮来调用它,如下所示:
private void btn_Connect_Click(object sender, EventArgs e)
{
Connect();
}
您只需将该按钮代码更改为:
private async void btn_Connect_Click(object sender, EventArgs e)
{
await Task.Run(()=> { Connect(); });
}
希望这有帮助!
更新:.NET 4.0及更高版本!
答案 1 :(得分:1)
当你的计时器事件被引发时会发生延迟,但没有数据需要读取。它会坐下来等到有。解决该问题的最佳方法是使用异步操作来处理I / O.例如:
public Form1()
{
InitializeComponent();
}
//Variables
TcpClient client;
StreamReader sr;
StreamWriter sw;
//Functions
public void connect(string host)
{
client = new TcpClient(host, 6667);
sr = new StreamReader(client.GetStream());
sw = new StreamWriter(client.GetStream());
}
public void write(string str)
{
textBox1.Text += str;
}
public void sendData(string str)
{
sw.WriteLine(str);
sw.Flush();
}
public async Task listener()
{
try
{
string data
while ((data = await sr.ReadLineAsync()) != null)
{
write(data);
}
}
catch (ObjectDisposedException)
{
// socket was closed forcefully
}
}
//End Functions
private void Form1_Load(object sender, EventArgs e)
{
//Initialize
write("Welcome to LogernIRC. Type \"/help\" for help with commands.\r\n");
}
private void button1_Click(object sender, EventArgs e) //Submit button clicked
{
//TextBox 1 is the text area , textbox 2 is the message/command area
//Command Area
if (textBox2.Text == "/help")
{
write("Help:\r\n/connect Connect to IRC server\r\n/help Display this help menu\r\n/join Join channel");
}
if (textBox2.Text.StartsWith("/connect"))
{
write("\r\nConnecting to " + textBox2.Text.Split(' ')[1] + " on port 6667...");
connect(textBox2.Text.Split(' ')[1]);
}
if (textBox2.Text.StartsWith("/join"))
{
write("\r\nJoining channel " + textBox2.Text.Split(' ')[1]);
}
if (textBox2.Text == "/test")
{
connect("irc.freenode.net");
// initiate async reading (storing the returned Task in a variable
// prevents the compiler from complaining that we don't await the
// call).
var _ = listener();
write("\r\nActivating test function...");
sendData("NICK Logern");
sendData("USER Logern 0 * :LOGERN");
}
}
上面的例子省略了一些错误检查和其他细节,但这是你想要做的基本想法。