C#TcpClient更新(针对IRC客户端)
问题描述:
我基本上试图用C#,WinForms和TcpClient制作一个准系统IRC客户端,它将从irc服务器上的原始数据显示到文本区域(textbox1)。然而,我在更新代码上挣扎(从服务器读取流)。现在我有一个定时器运行一个函数(listener
),每100ms从TCP流中读取一个函数。然而,我的应用程序冻结,光标被取消,应用程序挂起试图获取更多的数据。那么更好的更新功能是什么?C#TcpClient更新(针对IRC客户端)
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();
}
}
}
答
延迟发生在您的计时器事件发生时,但没有要读取的数据。它会坐下来等待,直到有。解决此问题的最佳方法是使用异步操作来处理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");
}
}
上面的例子省去了一些错误检查和其他精巧的细节,但它是你想要做什么的基本思想。
+0
谢谢!它工作没有任何UI故障! – Logern 2014-12-02 02:04:25
答
它没有运行异步,对不对?所以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(); });
}
希望这有助于!
UPDATE:.NET 4.0及以上版本!
您是否试图调试您的程序以明确发生挂起的位置? – 2014-12-01 23:42:02
对不起,修复的错误太多了。更好地搜索简单的客户端/服务器代码示例以开始。 – 2014-12-01 23:42:33
@ L.B或用于IRC的[预制C#库](https://github.com/meebey/SmartIrc4net) – 2014-12-01 23:43:52