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(); 
     } 
    } 
} 
+0

您是否试图调试您的程序以明确发生挂起的位置? – 2014-12-01 23:42:02

+0

对不起,修复的错误太多了。更好地搜索简单的客户端/服务器代码示例以开始。 – 2014-12-01 23:42:33

+0

@ L.B或用于IRC的[预制C#库](https://github.com/meebey/SmartIrc4net) – 2014-12-01 23:43:52

延迟发生在您的计时器事件发生时,但没有要读取的数据。它会坐下来等待,直到有。解决此问题的最佳方法是使用异步操作来处理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及以上版本!

+0

它适用于.NET 4.5吗? – Logern 2014-12-02 00:42:21

+0

现在我得到System.Reflection.TargetInvocationException – Logern 2014-12-02 00:49:08

+0

是的,.NET 4.0及以上! – 2014-12-02 00:49:17