【问题标题】:Windows Form chat application Using threadsWindows 窗体聊天应用程序使用线程
【发布时间】:2017-04-02 17:36:02
【问题描述】:

我是 C# 新手,我想使用线程编写一个聊天程序,其中每侧有两个线程(一个用于监听,另一个用于发送消息)。

但是,线程无法与组件(即文本框)一起使用,因为另一个线程已经捕获了它。

这是我的代码:

服务器端:

private TcpClient clientSocket;

private void btnStartListening_Click(object sender, EventArgs e)
{
    TcpListener serverSocket = new TcpListener(int.Parse(txtPort.Text));
    int requestCount = 0;
    clientSocket = default(TcpClient);
    serverSocket.Start();
    txtReceivedMessages.Text +=" >> Server Started\n";
    clientSocket = serverSocket.AcceptTcpClient();
    txtReceivedMessages.Text += " >> Accept connection from client\n";
    requestCount = 0;
    Thread t = new Thread(listeningThread);          
    t.Start();      
    clientSocket.Close();
    serverSocket.Stop();                    
}

private void listeningThread()
{
    while (true)
    {
        try
        {                  
            NetworkStream networkStream = clientSocket.GetStream();
            byte[] bytesFrom = new byte[10025];
            networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
            string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
            dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));                
            string serverResponse = "Last Message from client" + dataFromClient;                
            networkStream.Flush();                
            txtReceivedMessages.Text += dataFromClient;
            txtReceivedMessages.Update();
        }
        catch (Exception ex)
        {
            txtReceivedMessages.Text += ex.ToString() + "\n";
        }
    }
}

private void btnSend_Click(object sender, EventArgs e)
{
    NetworkStream networkStream = clientSocket.GetStream();
    Byte[] data = Encoding.ASCII.GetBytes(txtSendMessage.Text);
    networkStream.Write(data, 0, data.Length);
}

客户端:

private NetworkStream serverStream;
private void btnSend_Click(object sender, EventArgs e)
{
    serverStream = clientSocket.GetStream();
    byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtMsgToSend.Text + "$");
    serverStream.Write(outStream, 0, outStream.Length);
    serverStream.Flush();


}

private void listeningThread()
{
    byte[] inStream = new byte[10025];
        serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
        string returndata = System.Text.Encoding.ASCII.GetString(inStream);
        txtReceivedMsgs.Text += ">>" + returndata;
        txtMsgToSend.Text = "";
        txtMsgToSend.Focus();
}

private void btnSetConfiguration_Click(object sender, EventArgs e)
{
    txtReceivedMsgs.Text += "Client Started";
    clientSocket.Connect(txtIP.Text, int.Parse(txtPort.Text));
    lblStatus.Text = "Client Socket Program - Server Connected ...";
}

【问题讨论】:

  • 你还没有问过问题。
  • 您需要研究InvokeBeginInvoke 方法。他们可以执行在创建您想要操作的控件的主线程上引发异常的代码,例如TextBox
  • 亲爱的@Mong Zhu 我在listeningThread函数(服务器端)的第一行有一个错误:无法访问已处置的对象。
  • 问题是你在btnStartListening_Click方法中关闭了socket:clientSocket.Close();。然后你启动线程并尝试访问关闭的clientSocket。不要关闭或处置您当前正在使用的对象。

标签: c# multithreading sockets chat


【解决方案1】:

如果你想从另一个线程更新表单控件,你需要使用 Invoke() 方法。

查看更多关于 SO 的帖子 How to update the GUI from another thread in C#?

编辑:来自链接帖子的内容

对于 .NET 2.0,这是我编写的一段很好的代码,它完全符合您的要求,并且适用于 Control 上的任何属性:

private delegate void SetControlPropertyThreadSafeDelegate(
                         Control control, 
                         string propertyName, 
                         object propertyValue);

public static void SetControlPropertyThreadSafe(
                         Control control, 
                         string propertyName, 
                         object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
    propertyName, 
    BindingFlags.SetProperty, 
    null, 
    control, 
    new object[] { propertyValue });
  }
}

这样称呼它:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

如果您使用的是 .NET 3.0 或更高版本,您可以将上述方法重写为 Control 类的扩展方法,这样可以简化对以下的调用:

myLabel.SetPropertyThreadSafe("Text", status);

2010 年 5 月 10 日更新:

对于 .NET 3.0,您应该使用以下代码:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
                          Control @this, 
                          Expression<Func<TResult>> property, 
                          TResult value);

public static void SetPropertyThreadSafe<TResult>(
                          this Control @this, 
                          Expression<Func<TResult>> property, 
                          TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
  as PropertyInfo;

  if (propertyInfo == null ||
      !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
      propertyInfo.Name, 
      propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must      reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
  @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
  (SetPropertyThreadSafe), 
  new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
      propertyInfo.Name, 
      BindingFlags.SetProperty, 
      null, 
      @this, 
      new object[] { value });
  }
}

它使用 LINQ 和 lambda 表达式来实现更简洁、更简单和更安全的语法:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

现在不仅在编译时检查属性名称,还检查属性的类型,因此不可能(例如)将字符串值分配给布尔属性,从而导致运行时异常。

不幸的是,这并不能阻止任何人做一些愚蠢的事情,比如传入另一个控件的属性和值,所以下面的代码会很高兴地编译:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

因此,我添加了运行时检查以确保传入的属性确实属于调用该方法的控件。不完美,但仍然比 .NET 2.0 版本好很多。

如果有人对如何改进此代码以提高编译时安全性有任何进一步的建议,请发表评论!

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
  • 我觉得我没有说他需要使用 Invoke 方法?不过我可以编辑我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
相关资源
最近更新 更多