1

I have create a LAN chat application using thread base on code in C# Network Programming - Richard Blum. But when I run the program (I open 2 window chat application), click Connect in one side and suddenly the code crash with error "Additional information: Cross-thread operation not valid: Control 'lst_show' accessed from a thread other than the thread it was created on."

Some one help me, I try hours before ask in here but still can't solve it.

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.Net;
using System.Net.Sockets;
using System.Threading;

namespace Chat_Threads
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
        private static TextBox newText = new TextBox();
        private static ListBox results = new ListBox();
        private static Socket client;
        private static byte[] data = new byte[1024];


        private void btn_listen_Click(object sender, EventArgs e)
        {
            lst_show.Items.Add("Listening for a client...");

            Socket newsock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

            IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);

            newsock.Bind(iep);

            newsock.Listen(5);

            newsock.BeginAccept(new AsyncCallback(AcceptConn),newsock);   


        }

        private void AcceptConn(IAsyncResult ar)
        {
            Socket oldserver = (Socket)ar.AsyncState;

            client = oldserver.EndAccept(ar);

            lst_show.Items.Add("Connection from: " + client.RemoteEndPoint.ToString());

            Thread receiver = new Thread(new ThreadStart(ReceiveData));

            receiver.Start();
        }

        private void ReceiveData()
        {
            int recv;
            string stringData;
            while (true)
            {
                recv = client.Receive(data);
                stringData = Encoding.ASCII.GetString(data, 0, recv);
                if (stringData == "bye")
                    break;
                lst_show.Items.Add(stringData);
            }
            stringData = "bye";
            byte[] message = Encoding.ASCII.GetBytes(stringData);
            client.Send(message);
            client.Close();
            lst_show.Items.Add("Connection stopped");
            return;
        }

        private void btn_connect_Click(object sender, EventArgs e)
        {
            lst_show.Items.Add("Connecting...");

            client = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

            IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"),9050);

            client.BeginConnect(iep, new AsyncCallback(Connected),client);
        }

        private void Connected(IAsyncResult ar)
        {
            try
            {
                client.EndConnect(ar);

                this.lst_show.Items.Add("Connected to: " + client.RemoteEndPoint.ToString());

                Thread receiver = new Thread(new ThreadStart(ReceiveData));
                receiver.Start();

            }
            catch (SocketException)
            {
                this.lst_show.Items.Add("Error connecting");
            }
        }

        private void btn_send_Click(object sender, EventArgs e)
        {
            byte[] message = Encoding.ASCII.GetBytes(txt_message.Text);

            txt_message.Clear();

            client.BeginSend(message, 0, message.Length, 0, new AsyncCallback(SendData),client);

        }

        private void SendData(IAsyncResult ar)
        {
            Socket remote = (Socket)ar.AsyncState;
            int sent = remote.EndSend(ar);
        }


    }
}
Co May Hoa
  • 33
  • 1
  • 6

2 Answers2

0

Most UI engines have a main thread with a dedicated processing loop to update the user interface. That is because, if multiple threads try to make changes, that could create race conditions or deadlocks.

Your business logic typically runs on a separate thread, especially when you're doing things like TCP/IP communication. When the business logic needs to make a change to the UI, it does so in an indirect way, like putting a message on a queue, or invoking a special method.

In .NET, you can use the Invoke method to run code in the main UI thread, and then it will safely execute it when it gets the chance. For example:

lst_show.Items.Invoke((MethodInvoker)delegate 
{
    lst_show.Items.Add("Connection from: " + client.RemoteEndPoint.ToString());
});
drone6502
  • 433
  • 2
  • 7
0

The caller is on a different thread so you are going to have to makes sure the the list and the caller on the main UI thread so the following is

control.Invoke required.

     delegate void UpdateStringinTheGrid(string s);

      private void ListAddItem(string s)
        {
            if (lst_show.InvokeRequired())
            {
                var updateDel = new UpdateStringinTheGrid(ListAddItem);
                this.BeginInvoke(updateDel, s);
            }
          else
            lst_show.Items.Add(s);

}
    private void ReceiveData()
            {
                int recv;
                string stringData;
                while (true)
                {
                    recv = client.Receive(data);
                    stringData = Encoding.ASCII.GetString(data, 0, recv);
                    if (stringData == "bye")
                        break;
                   listAddString(stringData);
                }
                stringData = "bye";
                byte[] message = Encoding.ASCII.GetBytes(stringData);
                client.Send(message);
                client.Close();
                lst_show.Items.Add("Connection stopped");
                return;
            }
Stuart Smith
  • 1,931
  • 15
  • 26